summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/desktop_capture
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /third_party/libwebrtc/modules/desktop_capture
parentInitial commit. (diff)
downloadfirefox-43a97878ce14b72f0981164f87f2e35e14151312.tar.xz
firefox-43a97878ce14b72f0981164f87f2e35e14151312.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/libwebrtc/modules/desktop_capture')
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/BUILD.gn752
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/DEPS19
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/OWNERS2
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.cc139
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h83
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/blank_detector_desktop_capturer_wrapper_unittest.cc165
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/cropped_desktop_frame.cc66
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/cropped_desktop_frame.h33
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/cropped_desktop_frame_unittest.cc115
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/cropping_window_capturer.cc135
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/cropping_window_capturer.h84
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/cropping_window_capturer_win.cc327
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_and_cursor_composer.cc260
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_and_cursor_composer.h98
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc479
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capture_differ_sse2_gn/moz.build142
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capture_gn/moz.build439
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capture_metadata.h31
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capture_metrics_helper.cc60
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capture_metrics_helper.h22
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capture_objc_gn/moz.build72
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.cc61
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.h244
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capture_types.h77
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capturer.cc122
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capturer.h188
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capturer_differ_wrapper.cc232
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capturer_differ_wrapper.h72
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capturer_differ_wrapper_unittest.cc291
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capturer_wrapper.cc60
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_capturer_wrapper.h48
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_frame.cc209
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_frame.h227
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_frame_generator.cc184
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_frame_generator.h121
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_frame_rotation.cc117
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_frame_rotation.h52
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_frame_rotation_unittest.cc449
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_frame_unittest.cc336
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_frame_win.cc73
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_frame_win.h49
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_geometry.cc79
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_geometry.h169
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_geometry_unittest.cc106
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_region.cc567
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_region.h169
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/desktop_region_unittest.cc834
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/differ_block.cc71
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/differ_block.h42
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/differ_block_unittest.cc89
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/differ_vector_sse2.cc102
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/differ_vector_sse2.h31
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/fake_desktop_capturer.cc84
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/fake_desktop_capturer.h76
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/fallback_desktop_capturer_wrapper.cc183
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/fallback_desktop_capturer_wrapper.h64
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/fallback_desktop_capturer_wrapper_unittest.cc207
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/full_screen_application_handler.cc30
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/full_screen_application_handler.h50
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.cc84
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.h83
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc158
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h74
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/drm.sigs11
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/egl_dmabuf.cc703
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/egl_dmabuf.h68
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.cc59
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h44
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.cc1081
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.h182
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/pipewire.sigs50
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/pipewire_stub_header.fragment9
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/portal_request_response.h34
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/restore_token_manager.cc33
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/restore_token_manager.h41
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/scoped_glib.cc57
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/scoped_glib.h65
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc127
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h72
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_portal.cc455
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_portal.h213
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc132
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_stream_utils.h62
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc870
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.h76
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.cc195
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h111
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/wayland/xdg_session_details.h33
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc255
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h68
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.cc512
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.h147
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.cc102
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.h84
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc255
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.h78
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.cc52
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.h35
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.cc198
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.h56
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.cc51
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.h45
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.cc70
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.h51
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc379
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.h89
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.cc43
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.h63
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.h96
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.mm179
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc73
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h55
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.h58
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.mm108
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.h45
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.mm61
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.h59
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.mm70
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.cc238
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.h24
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.h128
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.mm552
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.cc430
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.h117
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mock_desktop_capturer_callback.cc23
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mock_desktop_capturer_callback.h39
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mouse_cursor.cc36
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mouse_cursor.h49
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor.h111
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc65
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm213
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_null.cc38
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc128
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc222
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/primitives_gn/moz.build185
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/resolution_tracker.cc34
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/resolution_tracker.h34
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/rgba_color.cc61
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/rgba_color.h59
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/rgba_color_unittest.cc45
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_capture_frame_queue.h75
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_capturer_darwin.mm33
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_capturer_fuchsia.cc415
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_capturer_fuchsia.h65
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_capturer_helper.cc90
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_capturer_helper.h91
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_capturer_helper_unittest.cc193
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_capturer_integration_test.cc380
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_capturer_linux.cc50
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_capturer_mac.mm766
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_capturer_mac_unittest.cc101
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_capturer_null.cc21
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_capturer_unittest.cc224
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_capturer_win.cc62
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_drawer.cc30
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_drawer.h79
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_drawer_linux.cc185
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_drawer_lock_posix.cc59
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_drawer_lock_posix.h39
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_drawer_mac.cc30
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_drawer_unittest.cc160
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/screen_drawer_win.cc209
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/shared_desktop_frame.cc59
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/shared_desktop_frame.h65
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/shared_memory.cc24
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/shared_memory.h82
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/test_utils.cc50
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/test_utils.h27
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/test_utils_unittest.cc110
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor.cc233
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor.h25
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_24bpp.curbin0 -> 3262 bytes
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_32bpp.curbin0 -> 4286 bytes
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_8bpp.curbin0 -> 2238 bytes
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_1bpp.curbin0 -> 326 bytes
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_32bpp.curbin0 -> 4286 bytes
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_32bpp.curbin0 -> 4286 bytes
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_4bpp.curbin0 -> 766 bytes
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest.cc91
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.h24
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.rc28
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/d3d_device.cc100
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/d3d_device.h59
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/desktop.cc111
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/desktop.h65
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.cc32
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.h29
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.cc37
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.h38
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc185
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h92
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.cc33
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.h62
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc514
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h253
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.cc77
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.h63
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc390
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.h149
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.cc81
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.h73
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc58
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.h47
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.cc132
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.h68
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.cc296
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.h24
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/scoped_gdi_object.h91
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.cc54
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.h55
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.cc184
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.h75
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils_unittest.cc81
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc230
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.h105
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx_unittest.cc41
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc243
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h85
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc398
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h140
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.cc59
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.h45
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.cc104
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.h51
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.cc423
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.h137
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.cc218
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.h141
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source_unittest.cc148
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.cc363
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.h166
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win_unittest.cc572
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.cc25
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.h46
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.cc486
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.h136
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils_unittest.cc153
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.cc400
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.h78
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/window_capturer_linux.cc50
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/window_capturer_mac.mm211
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/window_capturer_null.cc70
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/window_capturer_unittest.cc105
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/window_capturer_win.cc48
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/window_finder.cc20
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/window_finder.h65
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/window_finder_mac.h37
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/window_finder_mac.mm52
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/window_finder_unittest.cc178
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/window_finder_win.cc46
-rw-r--r--third_party/libwebrtc/modules/desktop_capture/window_finder_win.h30
251 files changed, 34826 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/desktop_capture/BUILD.gn b/third_party/libwebrtc/modules/desktop_capture/BUILD.gn
new file mode 100644
index 0000000000..8010db0fba
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/BUILD.gn
@@ -0,0 +1,752 @@
+# Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+#
+# Use of this source code is governed by a BSD-style license
+# that can be found in the LICENSE file in the root of the source
+# tree. An additional intellectual property rights grant can be found
+# in the file PATENTS. All contributing project authors may
+# be found in the AUTHORS file in the root of the source tree.
+
+import("//build/config/linux/gtk/gtk.gni")
+import("//build/config/linux/pkg_config.gni")
+import("//build/config/ui.gni")
+import("//tools/generate_stubs/rules.gni")
+import("../../webrtc.gni")
+
+use_desktop_capture_differ_sse2 = target_cpu == "x86" || target_cpu == "x64"
+
+config("x11_config") {
+ if (rtc_use_x11_extensions) {
+ defines = [ "WEBRTC_USE_X11" ]
+ }
+}
+
+rtc_library("primitives") {
+ visibility = [ "*" ]
+ sources = [
+ "desktop_capture_types.h",
+ "desktop_frame.cc",
+ "desktop_frame.h",
+ "desktop_geometry.cc",
+ "desktop_geometry.h",
+ "desktop_region.cc",
+ "desktop_region.h",
+ "shared_desktop_frame.cc",
+ "shared_desktop_frame.h",
+ "shared_memory.cc",
+ "shared_memory.h",
+ ]
+
+ deps = [
+ "../../api:scoped_refptr",
+ "../../rtc_base:checks",
+ "../../rtc_base:refcount",
+ "../../rtc_base/system:rtc_export",
+ "//third_party/libyuv",
+ ]
+ if (build_with_mozilla) {
+ deps -= [ "//third_party/libyuv" ]
+ include_dirs = [
+ "/media/libyuv",
+ "/media/libyuv/libyuv/include",
+ ]
+ }
+
+ if (!build_with_mozilla) {
+ deps += [ "../../rtc_base" ] # TODO(kjellander): Cleanup in
+ # bugs.webrtc.org/3806.
+ }
+}
+
+if (rtc_include_tests) {
+ rtc_library("desktop_capture_modules_tests") {
+ testonly = true
+
+ defines = []
+ sources = []
+ deps = [
+ ":desktop_capture",
+ "../../api:function_view",
+ "../../api:scoped_refptr",
+ "../../rtc_base:checks",
+ "../../rtc_base:logging",
+ "../../rtc_base:platform_thread",
+ "../../rtc_base:random",
+ "../../rtc_base:timeutils",
+ ]
+ if (rtc_desktop_capture_supported) {
+ deps += [
+ ":desktop_capture_mock",
+ ":primitives",
+ ":screen_drawer",
+ "../../rtc_base",
+ "../../rtc_base/third_party/base64",
+ "../../system_wrappers",
+ "../../test:test_support",
+ "../../test:video_test_support",
+ ]
+ sources += [
+ "screen_capturer_integration_test.cc",
+ "screen_drawer_unittest.cc",
+ "window_finder_unittest.cc",
+ ]
+
+ if ((is_linux || is_chromeos) && rtc_use_pipewire) {
+ configs += [ ":gio" ]
+ }
+
+ public_configs = [ ":x11_config" ]
+
+ if (is_win) {
+ deps += [ "../../rtc_base/win:windows_version" ]
+ }
+ }
+ }
+
+ rtc_library("desktop_capture_unittests") {
+ testonly = true
+
+ defines = []
+ sources = [
+ "blank_detector_desktop_capturer_wrapper_unittest.cc",
+ "cropped_desktop_frame_unittest.cc",
+ "desktop_and_cursor_composer_unittest.cc",
+ "desktop_capturer_differ_wrapper_unittest.cc",
+ "desktop_frame_rotation_unittest.cc",
+ "desktop_frame_unittest.cc",
+ "desktop_geometry_unittest.cc",
+ "desktop_region_unittest.cc",
+ "differ_block_unittest.cc",
+ "fallback_desktop_capturer_wrapper_unittest.cc",
+ "mouse_cursor_monitor_unittest.cc",
+ "rgba_color_unittest.cc",
+ "test_utils.cc",
+ "test_utils.h",
+ "test_utils_unittest.cc",
+ ]
+
+ if ((is_linux || is_chromeos) && rtc_use_pipewire) {
+ configs += [ ":gio" ]
+ }
+
+ deps = [
+ ":desktop_capture",
+ ":desktop_capture_mock",
+ ":primitives",
+ "../../rtc_base:checks",
+ "../../rtc_base:logging",
+ "../../rtc_base:macromagic",
+ "../../rtc_base:random",
+ "../../rtc_base:timeutils",
+
+ # TODO(bugs.webrtc.org/9987): Remove this dep on rtc_base:rtc_base once
+ # rtc_base:threading is fully defined.
+ "../../rtc_base:rtc_base",
+ "../../rtc_base:task_queue_for_test",
+ "../../rtc_base:threading",
+ "../../system_wrappers",
+ "../../test:test_support",
+ ]
+
+ if (is_win) {
+ sources += [
+ "win/cursor_unittest.cc",
+ "win/cursor_unittest_resources.h",
+ "win/cursor_unittest_resources.rc",
+ "win/screen_capture_utils_unittest.cc",
+ "win/screen_capturer_win_directx_unittest.cc",
+ "win/test_support/test_window.cc",
+ "win/test_support/test_window.h",
+ "win/window_capture_utils_unittest.cc",
+ ]
+ deps += [
+ "../../rtc_base/win:scoped_com_initializer",
+ "../../rtc_base/win:windows_version",
+ ]
+ }
+
+ if (rtc_desktop_capture_supported) {
+ sources += [
+ "screen_capturer_helper_unittest.cc",
+ "screen_capturer_unittest.cc",
+ "window_capturer_unittest.cc",
+ ]
+ if (is_mac) {
+ sources += [ "screen_capturer_mac_unittest.cc" ]
+ }
+ if (rtc_enable_win_wgc) {
+ sources += [
+ "win/wgc_capture_source_unittest.cc",
+ "win/wgc_capturer_win_unittest.cc",
+ ]
+ }
+ deps += [
+ ":desktop_capture_mock",
+ "../../system_wrappers:metrics",
+ ]
+ public_configs = [ ":x11_config" ]
+ }
+ }
+
+ rtc_library("screen_drawer") {
+ testonly = true
+
+ sources = [
+ "screen_drawer.cc",
+ "screen_drawer.h",
+ ]
+
+ if (is_linux || is_chromeos) {
+ sources += [ "screen_drawer_linux.cc" ]
+ libs = [ "X11" ]
+ }
+
+ if (is_mac) {
+ sources += [ "screen_drawer_mac.cc" ]
+ }
+
+ if (is_win) {
+ sources += [ "screen_drawer_win.cc" ]
+ }
+
+ deps = [
+ ":desktop_capture",
+ ":primitives",
+ "../../api:scoped_refptr",
+ "../../rtc_base:checks",
+ "../../rtc_base:logging",
+ "../../system_wrappers",
+ ]
+ absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
+
+ if (is_posix || is_fuchsia) {
+ sources += [
+ "screen_drawer_lock_posix.cc",
+ "screen_drawer_lock_posix.h",
+ ]
+ }
+ }
+
+ rtc_library("desktop_capture_mock") {
+ testonly = true
+
+ sources = [
+ "mock_desktop_capturer_callback.cc",
+ "mock_desktop_capturer_callback.h",
+ ]
+
+ if ((is_linux || is_chromeos) && rtc_use_pipewire) {
+ configs += [ ":gio" ]
+ }
+
+ deps = [
+ ":desktop_capture",
+ ":primitives",
+ "../../test:test_support",
+ ]
+ }
+}
+
+if (is_linux || is_chromeos) {
+ if (rtc_use_pipewire) {
+ defines = [ "WEBRTC_USE_PIPEWIRE" ]
+ if (!build_with_mozilla) {
+ pkg_config("gio") {
+ packages = [
+ "gio-2.0",
+ "gio-unix-2.0",
+ ]
+ }
+
+ pkg_config("pipewire") {
+ packages = [ "libpipewire-0.3" ]
+ if (!rtc_link_pipewire) {
+ ignore_libs = true
+ }
+ }
+ }
+
+if (!build_with_mozilla) {
+ pkg_config("gbm") {
+ packages = [ "gbm" ]
+ }
+ pkg_config("egl") {
+ packages = [ "egl" ]
+ }
+ pkg_config("epoxy") {
+ packages = [ "epoxy" ]
+ ignore_libs = true
+ }
+ pkg_config("libdrm") {
+ packages = [ "libdrm" ]
+ if (!rtc_link_pipewire) {
+ ignore_libs = true
+ }
+ }
+}
+
+ if (!rtc_link_pipewire) {
+ # When libpipewire is not directly linked, use stubs to allow for dlopening of
+ # the binary.
+ if (!build_with_mozilla) {
+ generate_stubs("pipewire_stubs") {
+ configs = [
+ "../../:common_config",
+ ":pipewire",
+ ":libdrm",
+ ]
+ deps = [ "../../rtc_base" ]
+ extra_header = "linux/wayland/pipewire_stub_header.fragment"
+ logging_function = "RTC_LOG(LS_VERBOSE)"
+ logging_include = "rtc_base/logging.h"
+ output_name = "linux/wayland/pipewire_stubs"
+ path_from_source = "modules/desktop_capture/linux/wayland"
+ sigs = [
+ "linux/wayland/pipewire.sigs",
+ "linux/wayland/drm.sigs",
+ ]
+ if (!build_with_chromium) {
+ macro_include = "rtc_base/system/no_cfi_icall.h"
+ macro_deps = [ "../../rtc_base/system:no_cfi_icall" ]
+ }
+ }
+ }
+ }
+
+ config("pipewire_config") {
+ if (!build_with_mozilla) {
+ defines = [ "WEBRTC_USE_PIPEWIRE" ]
+ }
+ if (!rtc_link_pipewire) {
+ defines += [ "WEBRTC_DLOPEN_PIPEWIRE" ]
+ }
+
+ # Chromecast build config overrides `WEBRTC_USE_PIPEWIRE` even when
+ # `rtc_use_pipewire` is not set, which causes pipewire_config to not be
+ # included in targets. More details in: webrtc:13898
+ if (is_linux && !is_castos) {
+ defines += [ "WEBRTC_USE_GIO" ]
+ }
+ }
+ }
+}
+
+rtc_library("desktop_capture") {
+ visibility = [ "*" ]
+ defines = []
+ include_dirs = []
+ if (build_with_mozilla) {
+ include_dirs += [ "/media/libyuv/libyuv/include" ]
+ }
+ deps = []
+ public_configs = [ ":x11_config" ]
+ sources = [
+ "blank_detector_desktop_capturer_wrapper.cc",
+ "blank_detector_desktop_capturer_wrapper.h",
+ "cropped_desktop_frame.cc",
+ "cropped_desktop_frame.h",
+ "cropping_window_capturer.cc",
+ "cropping_window_capturer.h",
+ "desktop_and_cursor_composer.cc",
+ "desktop_and_cursor_composer.h",
+ "desktop_capture_metrics_helper.cc",
+ "desktop_capture_metrics_helper.h",
+ "desktop_capture_options.cc",
+ "desktop_capture_options.h",
+ "desktop_capturer.cc",
+ "desktop_capturer.h",
+ "desktop_capturer_differ_wrapper.cc",
+ "desktop_capturer_differ_wrapper.h",
+ "desktop_capturer_wrapper.cc",
+ "desktop_capturer_wrapper.h",
+ "desktop_frame_generator.cc",
+ "desktop_frame_generator.h",
+ "desktop_frame_rotation.cc",
+ "desktop_frame_rotation.h",
+ "differ_block.cc",
+ "differ_block.h",
+ "fake_desktop_capturer.cc",
+ "fake_desktop_capturer.h",
+ "fallback_desktop_capturer_wrapper.cc",
+ "fallback_desktop_capturer_wrapper.h",
+ "full_screen_application_handler.cc",
+ "full_screen_application_handler.h",
+ "full_screen_window_detector.cc",
+ "full_screen_window_detector.h",
+ "mouse_cursor.cc",
+ "mouse_cursor.h",
+ "mouse_cursor_monitor.h",
+ "resolution_tracker.cc",
+ "resolution_tracker.h",
+ "rgba_color.cc",
+ "rgba_color.h",
+ "screen_capture_frame_queue.h",
+ "screen_capturer_helper.cc",
+ "screen_capturer_helper.h",
+ "window_finder.cc",
+ "window_finder.h",
+ ]
+ if (is_linux && !is_castos && rtc_use_pipewire) {
+ sources += [ "desktop_capture_metadata.h" ]
+ }
+ if (is_mac) {
+ sources += [
+ "mac/desktop_configuration.h",
+ "mac/desktop_configuration_monitor.cc",
+ "mac/desktop_configuration_monitor.h",
+ "mac/full_screen_mac_application_handler.cc",
+ "mac/full_screen_mac_application_handler.h",
+ "mac/window_list_utils.cc",
+ "mac/window_list_utils.h",
+ ]
+ deps += [ ":desktop_capture_objc" ]
+ }
+ if (rtc_use_x11_extensions || rtc_use_pipewire) {
+ include_dirs += [ "/third_party/libwebrtc/third_party/pipewire" ]
+ sources += [
+ "mouse_cursor_monitor_linux.cc",
+ "screen_capturer_linux.cc",
+ "window_capturer_linux.cc",
+ ]
+ }
+
+ if (rtc_use_x11_extensions) {
+ sources += [
+ "linux/x11/mouse_cursor_monitor_x11.cc",
+ "linux/x11/mouse_cursor_monitor_x11.h",
+ "linux/x11/screen_capturer_x11.cc",
+ "linux/x11/screen_capturer_x11.h",
+ "linux/x11/shared_x_display.cc",
+ "linux/x11/shared_x_display.h",
+ "linux/x11/window_capturer_x11.cc",
+ "linux/x11/window_capturer_x11.h",
+ "linux/x11/window_finder_x11.cc",
+ "linux/x11/window_finder_x11.h",
+ "linux/x11/window_list_utils.cc",
+ "linux/x11/window_list_utils.h",
+ "linux/x11/x_atom_cache.cc",
+ "linux/x11/x_atom_cache.h",
+ "linux/x11/x_error_trap.cc",
+ "linux/x11/x_error_trap.h",
+ "linux/x11/x_server_pixel_buffer.cc",
+ "linux/x11/x_server_pixel_buffer.h",
+ "linux/x11/x_window_property.cc",
+ "linux/x11/x_window_property.h",
+ ]
+ libs = [
+ "X11",
+ "Xcomposite",
+ "Xdamage",
+ "Xext",
+ "Xfixes",
+ "Xrender",
+ "Xrandr",
+ "Xtst",
+ ]
+ }
+
+ if (!is_win && !is_mac && !rtc_use_x11_extensions && !rtc_use_pipewire &&
+ !is_fuchsia) {
+ sources += [
+ "mouse_cursor_monitor_null.cc",
+ "screen_capturer_null.cc",
+ "window_capturer_null.cc",
+ ]
+ }
+
+ deps += [
+ ":primitives",
+ "../../api:function_view",
+ "../../api:make_ref_counted",
+ "../../api:refcountedbase",
+ "../../api:scoped_refptr",
+ "../../api:sequence_checker",
+ "../../rtc_base:checks",
+ "../../rtc_base:event_tracer",
+ "../../rtc_base:logging",
+ "../../rtc_base:macromagic",
+ "../../rtc_base:random",
+ "../../rtc_base:stringutils",
+ "../../rtc_base:timeutils",
+ "../../rtc_base/synchronization:mutex",
+ "../../rtc_base/system:arch",
+ "../../rtc_base/system:no_unique_address",
+ "../../rtc_base/system:rtc_export",
+ "../../system_wrappers",
+ "../../system_wrappers:metrics",
+ ]
+
+ if (is_fuchsia) {
+ sources += [
+ "mouse_cursor_monitor_null.cc",
+ "screen_capturer_fuchsia.cc",
+ "screen_capturer_fuchsia.h",
+ "window_capturer_null.cc",
+ ]
+ deps += [
+ "../../rtc_base:divide_round",
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.sysmem",
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.composition",
+ "//third_party/fuchsia-sdk/sdk/fidl/fuchsia.ui.scenic",
+ "//third_party/fuchsia-sdk/sdk/pkg/scenic_cpp",
+ "//third_party/fuchsia-sdk/sdk/pkg/sys_cpp",
+ ]
+ }
+
+ if (is_win) {
+ sources += [
+ "cropping_window_capturer_win.cc",
+ "desktop_frame_win.cc",
+ "desktop_frame_win.h",
+ "mouse_cursor_monitor_win.cc",
+ "screen_capturer_win.cc",
+ "win/cursor.cc",
+ "win/cursor.h",
+ "win/d3d_device.cc",
+ "win/d3d_device.h",
+ "win/desktop.cc",
+ "win/desktop.h",
+ "win/desktop_capture_utils.cc",
+ "win/desktop_capture_utils.h",
+ "win/display_configuration_monitor.cc",
+ "win/display_configuration_monitor.h",
+ "win/dxgi_adapter_duplicator.cc",
+ "win/dxgi_adapter_duplicator.h",
+ "win/dxgi_context.cc",
+ "win/dxgi_context.h",
+ "win/dxgi_duplicator_controller.cc",
+ "win/dxgi_duplicator_controller.h",
+ "win/dxgi_frame.cc",
+ "win/dxgi_frame.h",
+ "win/dxgi_output_duplicator.cc",
+ "win/dxgi_output_duplicator.h",
+ "win/dxgi_texture.cc",
+ "win/dxgi_texture.h",
+ "win/dxgi_texture_mapping.cc",
+ "win/dxgi_texture_mapping.h",
+ "win/dxgi_texture_staging.cc",
+ "win/dxgi_texture_staging.h",
+ "win/full_screen_win_application_handler.cc",
+ "win/full_screen_win_application_handler.h",
+ "win/scoped_gdi_object.h",
+ "win/scoped_thread_desktop.cc",
+ "win/scoped_thread_desktop.h",
+ "win/screen_capture_utils.cc",
+ "win/screen_capture_utils.h",
+ "win/screen_capturer_win_directx.cc",
+ "win/screen_capturer_win_directx.h",
+ "win/screen_capturer_win_gdi.cc",
+ "win/screen_capturer_win_gdi.h",
+ "win/screen_capturer_win_magnifier.cc",
+ "win/screen_capturer_win_magnifier.h",
+ "win/selected_window_context.cc",
+ "win/selected_window_context.h",
+ "win/window_capture_utils.cc",
+ "win/window_capture_utils.h",
+ "win/window_capturer_win_gdi.cc",
+ "win/window_capturer_win_gdi.h",
+ "window_capturer_win.cc",
+ "window_finder_win.cc",
+ "window_finder_win.h",
+ ]
+ libs = [
+ "d3d11.lib",
+ "dxgi.lib",
+ ]
+ deps += [
+ "../../rtc_base:win32",
+ "../../rtc_base/win:create_direct3d_device",
+ "../../rtc_base/win:get_activation_factory",
+ "../../rtc_base/win:windows_version",
+ ]
+ }
+
+ absl_deps = [
+ "//third_party/abseil-cpp/absl/memory",
+ "//third_party/abseil-cpp/absl/strings",
+ "//third_party/abseil-cpp/absl/types:optional",
+ ]
+
+ if (rtc_use_x11_extensions) {
+ deps += [ "../../rtc_base:sanitizer" ]
+ }
+
+ if (!build_with_mozilla) {
+ deps += [ "//third_party/libyuv" ]
+ } else {
+ include_dirs += [
+ "/media/libyuv",
+ "/media/libyuv/libyuv/include",
+ "/third_party/pipewire",
+ ]
+ }
+
+ if (use_desktop_capture_differ_sse2) {
+ deps += [ ":desktop_capture_differ_sse2" ]
+ }
+
+ if (rtc_use_pipewire) {
+ if (!build_with_mozilla) {
+ sources += [
+ "linux/wayland/base_capturer_pipewire.cc",
+ "linux/wayland/base_capturer_pipewire.h",
+ "linux/wayland/egl_dmabuf.cc",
+ "linux/wayland/egl_dmabuf.h",
+ "linux/wayland/mouse_cursor_monitor_pipewire.cc",
+ "linux/wayland/mouse_cursor_monitor_pipewire.h",
+ "linux/wayland/portal_request_response.h",
+ "linux/wayland/restore_token_manager.cc",
+ "linux/wayland/restore_token_manager.h",
+ "linux/wayland/scoped_glib.cc",
+ "linux/wayland/scoped_glib.h",
+ "linux/wayland/screen_capture_portal_interface.cc",
+ "linux/wayland/screen_capture_portal_interface.h",
+ "linux/wayland/screencast_portal.cc",
+ "linux/wayland/screencast_portal.h",
+ "linux/wayland/screencast_stream_utils.cc",
+ "linux/wayland/screencast_stream_utils.h",
+ "linux/wayland/shared_screencast_stream.cc",
+ "linux/wayland/shared_screencast_stream.h",
+ "linux/wayland/xdg_desktop_portal_utils.cc",
+ "linux/wayland/xdg_desktop_portal_utils.h",
+ "linux/wayland/xdg_session_details.h",
+ ]
+ } else {
+ sources += [
+ "linux/wayland/moz_base_capturer_pipewire.cc",
+ "linux/wayland/moz_base_capturer_pipewire.h",
+ ]
+ }
+
+ if (!build_with_mozilla) {
+ configs += [
+ ":gio",
+ ":pipewire",
+ ":gbm",
+ ":egl",
+ ":epoxy",
+ ":libdrm",
+ ]
+ }
+
+ if (!rtc_link_pipewire) {
+ if (!build_with_mozilla) {
+ deps += [ ":pipewire_stubs" ]
+ }
+
+ if (!build_with_mozilla) {
+ configs += [
+ ":gio",
+ ":pipewire",
+ ]
+ } else {
+ defines += [ "WEBRTC_USE_PIPEWIRE" ]
+ include_dirs += [ "/third_party/pipewire" ]
+ }
+ }
+
+ if (!build_with_mozilla) {
+ public_configs += [ ":pipewire_config" ]
+ }
+
+ deps += [ "../../rtc_base:sanitizer" ]
+ }
+
+ if (rtc_enable_win_wgc) {
+ sources += [
+ "win/wgc_capture_session.cc",
+ "win/wgc_capture_session.h",
+ "win/wgc_capture_source.cc",
+ "win/wgc_capture_source.h",
+ "win/wgc_capturer_win.cc",
+ "win/wgc_capturer_win.h",
+ "win/wgc_desktop_frame.cc",
+ "win/wgc_desktop_frame.h",
+ ]
+ libs += [ "dwmapi.lib" ]
+ deps += [
+ "../../rtc_base:rtc_event",
+ "../../rtc_base/win:hstring",
+ ]
+ }
+}
+
+if (is_mac) {
+ rtc_library("desktop_capture_objc") {
+ # This target, needs to be separated from ":desktop_capture" because
+ # that is the C++ part of the target while this one is the Obj-C++ part.
+ # Aside from this, both represent a "desktop_capture" target.
+ # This target separation based on programming languages introduces a
+ # dependency cycle between ":desktop_capture" and
+ # ":desktop_capture_objc".
+ # To break this, ":desktop_capture_objc" shares some .h files with
+ # ":desktop_capture" but when external targets need one of these
+ # headers, they should depend on ":desktop_capture" and consider
+ # this target as private.
+ visibility = [ ":desktop_capture" ]
+ sources = [
+ "desktop_capture_options.h",
+ "desktop_capturer.h",
+ "full_screen_application_handler.h",
+ "full_screen_window_detector.h",
+ "mac/desktop_configuration.h",
+ "mac/desktop_configuration.mm",
+ "mac/desktop_configuration_monitor.h",
+ "mac/desktop_frame_cgimage.h",
+ "mac/desktop_frame_cgimage.mm",
+ "mac/desktop_frame_iosurface.h",
+ "mac/desktop_frame_iosurface.mm",
+ "mac/desktop_frame_provider.h",
+ "mac/desktop_frame_provider.mm",
+ "mac/screen_capturer_mac.h",
+ "mac/screen_capturer_mac.mm",
+ "mac/window_list_utils.h",
+ "mouse_cursor.h",
+ "mouse_cursor_monitor.h",
+ "mouse_cursor_monitor_mac.mm",
+ "screen_capture_frame_queue.h",
+ "screen_capturer_darwin.mm",
+ "screen_capturer_helper.h",
+ "window_capturer_mac.mm",
+ "window_finder.h",
+ "window_finder_mac.h",
+ "window_finder_mac.mm",
+ ]
+ deps = [
+ ":primitives",
+ "../../api:function_view",
+ "../../api:refcountedbase",
+ "../../api:scoped_refptr",
+ "../../api:sequence_checker",
+ "../../rtc_base",
+ "../../rtc_base:checks",
+ "../../rtc_base:event_tracer",
+ "../../rtc_base:logging",
+ "../../rtc_base:macromagic",
+ "../../rtc_base:timeutils",
+ "../../rtc_base/synchronization:mutex",
+ "../../rtc_base/system:rtc_export",
+ "../../sdk:helpers_objc",
+ ]
+ frameworks = [
+ "AppKit.framework",
+ "IOKit.framework",
+ "IOSurface.framework",
+ ]
+ }
+}
+
+if (use_desktop_capture_differ_sse2) {
+ # Have to be compiled as a separate target because it needs to be compiled
+ # with SSE2 enabled.
+ rtc_library("desktop_capture_differ_sse2") {
+ visibility = [ ":*" ]
+ sources = [
+ "differ_vector_sse2.cc",
+ "differ_vector_sse2.h",
+ ]
+
+ if (is_posix || is_fuchsia) {
+ cflags = [ "-msse2" ]
+ }
+ }
+}
diff --git a/third_party/libwebrtc/modules/desktop_capture/DEPS b/third_party/libwebrtc/modules/desktop_capture/DEPS
new file mode 100644
index 0000000000..8c894c4430
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/DEPS
@@ -0,0 +1,19 @@
+include_rules = [
+ "+system_wrappers",
+ "+third_party/libyuv",
+]
+
+specific_include_rules = {
+ "desktop_frame_cgimage\.h": [
+ "+sdk/objc",
+ ],
+ "desktop_frame_iosurface\.h": [
+ "+sdk/objc",
+ ],
+ "desktop_frame_provider\.h": [
+ "+sdk/objc",
+ ],
+ "screen_capturer_mac\.mm": [
+ "+sdk/objc",
+ ],
+}
diff --git a/third_party/libwebrtc/modules/desktop_capture/OWNERS b/third_party/libwebrtc/modules/desktop_capture/OWNERS
new file mode 100644
index 0000000000..e3bc32ee5c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/OWNERS
@@ -0,0 +1,2 @@
+alcooper@chromium.org
+mfoltz@chromium.org
diff --git a/third_party/libwebrtc/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.cc b/third_party/libwebrtc/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.cc
new file mode 100644
index 0000000000..8e56ffc3fd
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.cc
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h"
+
+#include <stdint.h>
+
+#include <utility>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "rtc_base/checks.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+
+BlankDetectorDesktopCapturerWrapper::BlankDetectorDesktopCapturerWrapper(
+ std::unique_ptr<DesktopCapturer> capturer,
+ RgbaColor blank_pixel,
+ bool check_per_capture)
+ : capturer_(std::move(capturer)),
+ blank_pixel_(blank_pixel),
+ check_per_capture_(check_per_capture) {
+ RTC_DCHECK(capturer_);
+}
+
+BlankDetectorDesktopCapturerWrapper::~BlankDetectorDesktopCapturerWrapper() =
+ default;
+
+void BlankDetectorDesktopCapturerWrapper::Start(
+ DesktopCapturer::Callback* callback) {
+ callback_ = callback;
+ capturer_->Start(this);
+}
+
+void BlankDetectorDesktopCapturerWrapper::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
+ capturer_->SetSharedMemoryFactory(std::move(shared_memory_factory));
+}
+
+void BlankDetectorDesktopCapturerWrapper::CaptureFrame() {
+ RTC_DCHECK(callback_);
+ capturer_->CaptureFrame();
+}
+
+void BlankDetectorDesktopCapturerWrapper::SetExcludedWindow(WindowId window) {
+ capturer_->SetExcludedWindow(window);
+}
+
+bool BlankDetectorDesktopCapturerWrapper::GetSourceList(SourceList* sources) {
+ return capturer_->GetSourceList(sources);
+}
+
+bool BlankDetectorDesktopCapturerWrapper::SelectSource(SourceId id) {
+ if (check_per_capture_) {
+ // If we start capturing a new source, we must reset these members
+ // so we don't short circuit the blank detection logic.
+ is_first_frame_ = true;
+ non_blank_frame_received_ = false;
+ }
+
+ return capturer_->SelectSource(id);
+}
+
+bool BlankDetectorDesktopCapturerWrapper::FocusOnSelectedSource() {
+ return capturer_->FocusOnSelectedSource();
+}
+
+bool BlankDetectorDesktopCapturerWrapper::IsOccluded(const DesktopVector& pos) {
+ return capturer_->IsOccluded(pos);
+}
+
+void BlankDetectorDesktopCapturerWrapper::OnCaptureResult(
+ Result result,
+ std::unique_ptr<DesktopFrame> frame) {
+ RTC_DCHECK(callback_);
+ if (result != Result::SUCCESS || non_blank_frame_received_) {
+ callback_->OnCaptureResult(result, std::move(frame));
+ return;
+ }
+
+ if (!frame) {
+ // Capturer can call the blank detector with empty frame. Blank
+ // detector regards it as a blank frame.
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY,
+ std::unique_ptr<DesktopFrame>());
+ return;
+ }
+
+ // If nothing has been changed in current frame, we do not need to check it
+ // again.
+ if (!frame->updated_region().is_empty() || is_first_frame_) {
+ last_frame_is_blank_ = IsBlankFrame(*frame);
+ is_first_frame_ = false;
+ }
+ RTC_HISTOGRAM_BOOLEAN("WebRTC.DesktopCapture.BlankFrameDetected",
+ last_frame_is_blank_);
+ if (!last_frame_is_blank_) {
+ non_blank_frame_received_ = true;
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+ return;
+ }
+
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY,
+ std::unique_ptr<DesktopFrame>());
+}
+
+bool BlankDetectorDesktopCapturerWrapper::IsBlankFrame(
+ const DesktopFrame& frame) const {
+ // We will check 7489 pixels for a frame with 1024 x 768 resolution.
+ for (int i = 0; i < frame.size().width() * frame.size().height(); i += 105) {
+ const int x = i % frame.size().width();
+ const int y = i / frame.size().width();
+ if (!IsBlankPixel(frame, x, y)) {
+ return false;
+ }
+ }
+
+ // We are verifying the pixel in the center as well.
+ return IsBlankPixel(frame, frame.size().width() / 2,
+ frame.size().height() / 2);
+}
+
+bool BlankDetectorDesktopCapturerWrapper::IsBlankPixel(
+ const DesktopFrame& frame,
+ int x,
+ int y) const {
+ uint8_t* pixel_data = frame.GetFrameDataAtPos(DesktopVector(x, y));
+ return RgbaColor(pixel_data) == blank_pixel_;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h b/third_party/libwebrtc/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h
new file mode 100644
index 0000000000..d10f9cf725
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_BLANK_DETECTOR_DESKTOP_CAPTURER_WRAPPER_H_
+#define MODULES_DESKTOP_CAPTURE_BLANK_DETECTOR_DESKTOP_CAPTURER_WRAPPER_H_
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/rgba_color.h"
+#include "modules/desktop_capture/shared_memory.h"
+
+namespace webrtc {
+
+// A DesktopCapturer wrapper detects the return value of its owned
+// DesktopCapturer implementation. If sampled pixels returned by the
+// DesktopCapturer implementation all equal to the blank pixel, this wrapper
+// returns ERROR_TEMPORARY. If the DesktopCapturer implementation fails for too
+// many times, this wrapper returns ERROR_PERMANENT.
+class BlankDetectorDesktopCapturerWrapper final
+ : public DesktopCapturer,
+ public DesktopCapturer::Callback {
+ public:
+ // Creates BlankDetectorDesktopCapturerWrapper. BlankDesktopCapturerWrapper
+ // takes ownership of `capturer`. The `blank_pixel` is the unmodified color
+ // returned by the `capturer`.
+ BlankDetectorDesktopCapturerWrapper(std::unique_ptr<DesktopCapturer> capturer,
+ RgbaColor blank_pixel,
+ bool check_per_capture = false);
+ ~BlankDetectorDesktopCapturerWrapper() override;
+
+ // DesktopCapturer interface.
+ void Start(DesktopCapturer::Callback* callback) override;
+ void SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) override;
+ void CaptureFrame() override;
+ void SetExcludedWindow(WindowId window) override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+ bool FocusOnSelectedSource() override;
+ bool IsOccluded(const DesktopVector& pos) override;
+
+ private:
+ // DesktopCapturer::Callback interface.
+ void OnCaptureResult(Result result,
+ std::unique_ptr<DesktopFrame> frame) override;
+
+ bool IsBlankFrame(const DesktopFrame& frame) const;
+
+ // Detects whether pixel at (x, y) equals to `blank_pixel_`.
+ bool IsBlankPixel(const DesktopFrame& frame, int x, int y) const;
+
+ const std::unique_ptr<DesktopCapturer> capturer_;
+ const RgbaColor blank_pixel_;
+
+ // Whether a non-blank frame has been received.
+ bool non_blank_frame_received_ = false;
+
+ // Whether the last frame is blank.
+ bool last_frame_is_blank_ = false;
+
+ // Whether current frame is the first frame.
+ bool is_first_frame_ = true;
+
+ // Blank inspection is made per capture instead of once for all
+ // screens or windows.
+ bool check_per_capture_ = false;
+
+ DesktopCapturer::Callback* callback_ = nullptr;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_BLANK_DETECTOR_DESKTOP_CAPTURER_WRAPPER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/blank_detector_desktop_capturer_wrapper_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/blank_detector_desktop_capturer_wrapper_unittest.cc
new file mode 100644
index 0000000000..25a81edd89
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/blank_detector_desktop_capturer_wrapper_unittest.cc
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h"
+
+#include <memory>
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_frame_generator.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/fake_desktop_capturer.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+class BlankDetectorDesktopCapturerWrapperTest
+ : public ::testing::Test,
+ public DesktopCapturer::Callback {
+ public:
+ BlankDetectorDesktopCapturerWrapperTest();
+ ~BlankDetectorDesktopCapturerWrapperTest() override;
+
+ protected:
+ void PerfTest(DesktopCapturer* capturer);
+
+ const int frame_width_ = 1024;
+ const int frame_height_ = 768;
+ std::unique_ptr<BlankDetectorDesktopCapturerWrapper> wrapper_;
+ DesktopCapturer* capturer_ = nullptr;
+ BlackWhiteDesktopFramePainter painter_;
+ int num_frames_captured_ = 0;
+ DesktopCapturer::Result last_result_ = DesktopCapturer::Result::SUCCESS;
+ std::unique_ptr<DesktopFrame> last_frame_;
+
+ private:
+ // DesktopCapturer::Callback interface.
+ void OnCaptureResult(DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame> frame) override;
+
+ PainterDesktopFrameGenerator frame_generator_;
+};
+
+BlankDetectorDesktopCapturerWrapperTest::
+ BlankDetectorDesktopCapturerWrapperTest() {
+ frame_generator_.size()->set(frame_width_, frame_height_);
+ frame_generator_.set_desktop_frame_painter(&painter_);
+ std::unique_ptr<DesktopCapturer> capturer(new FakeDesktopCapturer());
+ FakeDesktopCapturer* fake_capturer =
+ static_cast<FakeDesktopCapturer*>(capturer.get());
+ fake_capturer->set_frame_generator(&frame_generator_);
+ capturer_ = fake_capturer;
+ wrapper_.reset(new BlankDetectorDesktopCapturerWrapper(
+ std::move(capturer), RgbaColor(0, 0, 0, 0)));
+ wrapper_->Start(this);
+}
+
+BlankDetectorDesktopCapturerWrapperTest::
+ ~BlankDetectorDesktopCapturerWrapperTest() = default;
+
+void BlankDetectorDesktopCapturerWrapperTest::OnCaptureResult(
+ DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame> frame) {
+ last_result_ = result;
+ last_frame_ = std::move(frame);
+ num_frames_captured_++;
+}
+
+void BlankDetectorDesktopCapturerWrapperTest::PerfTest(
+ DesktopCapturer* capturer) {
+ for (int i = 0; i < 10000; i++) {
+ capturer->CaptureFrame();
+ ASSERT_EQ(num_frames_captured_, i + 1);
+ }
+}
+
+TEST_F(BlankDetectorDesktopCapturerWrapperTest, ShouldDetectBlankFrame) {
+ wrapper_->CaptureFrame();
+ ASSERT_EQ(num_frames_captured_, 1);
+ ASSERT_EQ(last_result_, DesktopCapturer::Result::ERROR_TEMPORARY);
+ ASSERT_FALSE(last_frame_);
+}
+
+TEST_F(BlankDetectorDesktopCapturerWrapperTest, ShouldPassBlankDetection) {
+ painter_.updated_region()->AddRect(DesktopRect::MakeXYWH(0, 0, 100, 100));
+ wrapper_->CaptureFrame();
+ ASSERT_EQ(num_frames_captured_, 1);
+ ASSERT_EQ(last_result_, DesktopCapturer::Result::SUCCESS);
+ ASSERT_TRUE(last_frame_);
+
+ painter_.updated_region()->AddRect(
+ DesktopRect::MakeXYWH(frame_width_ - 100, frame_height_ - 100, 100, 100));
+ wrapper_->CaptureFrame();
+ ASSERT_EQ(num_frames_captured_, 2);
+ ASSERT_EQ(last_result_, DesktopCapturer::Result::SUCCESS);
+ ASSERT_TRUE(last_frame_);
+
+ painter_.updated_region()->AddRect(
+ DesktopRect::MakeXYWH(0, frame_height_ - 100, 100, 100));
+ wrapper_->CaptureFrame();
+ ASSERT_EQ(num_frames_captured_, 3);
+ ASSERT_EQ(last_result_, DesktopCapturer::Result::SUCCESS);
+ ASSERT_TRUE(last_frame_);
+
+ painter_.updated_region()->AddRect(
+ DesktopRect::MakeXYWH(frame_width_ - 100, 0, 100, 100));
+ wrapper_->CaptureFrame();
+ ASSERT_EQ(num_frames_captured_, 4);
+ ASSERT_EQ(last_result_, DesktopCapturer::Result::SUCCESS);
+ ASSERT_TRUE(last_frame_);
+
+ painter_.updated_region()->AddRect(DesktopRect::MakeXYWH(
+ (frame_width_ >> 1) - 50, (frame_height_ >> 1) - 50, 100, 100));
+ wrapper_->CaptureFrame();
+ ASSERT_EQ(num_frames_captured_, 5);
+ ASSERT_EQ(last_result_, DesktopCapturer::Result::SUCCESS);
+ ASSERT_TRUE(last_frame_);
+}
+
+TEST_F(BlankDetectorDesktopCapturerWrapperTest,
+ ShouldNotCheckAfterANonBlankFrameReceived) {
+ wrapper_->CaptureFrame();
+ ASSERT_EQ(num_frames_captured_, 1);
+ ASSERT_EQ(last_result_, DesktopCapturer::Result::ERROR_TEMPORARY);
+ ASSERT_FALSE(last_frame_);
+
+ painter_.updated_region()->AddRect(
+ DesktopRect::MakeXYWH(frame_width_ - 100, 0, 100, 100));
+ wrapper_->CaptureFrame();
+ ASSERT_EQ(num_frames_captured_, 2);
+ ASSERT_EQ(last_result_, DesktopCapturer::Result::SUCCESS);
+ ASSERT_TRUE(last_frame_);
+
+ for (int i = 0; i < 100; i++) {
+ wrapper_->CaptureFrame();
+ ASSERT_EQ(num_frames_captured_, i + 3);
+ ASSERT_EQ(last_result_, DesktopCapturer::Result::SUCCESS);
+ ASSERT_TRUE(last_frame_);
+ }
+}
+
+// There is no perceptible impact by using BlankDetectorDesktopCapturerWrapper.
+// i.e. less than 0.2ms per frame.
+// [ OK ] DISABLED_Performance (10210 ms)
+// [ OK ] DISABLED_PerformanceComparison (8791 ms)
+TEST_F(BlankDetectorDesktopCapturerWrapperTest, DISABLED_Performance) {
+ PerfTest(wrapper_.get());
+}
+
+TEST_F(BlankDetectorDesktopCapturerWrapperTest,
+ DISABLED_PerformanceComparison) {
+ capturer_->Start(this);
+ PerfTest(capturer_);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/cropped_desktop_frame.cc b/third_party/libwebrtc/modules/desktop_capture/cropped_desktop_frame.cc
new file mode 100644
index 0000000000..54488b7d62
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/cropped_desktop_frame.cc
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/cropped_desktop_frame.h"
+
+#include <memory>
+#include <utility>
+
+#include "modules/desktop_capture/desktop_region.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+// A DesktopFrame that is a sub-rect of another DesktopFrame.
+class CroppedDesktopFrame : public DesktopFrame {
+ public:
+ CroppedDesktopFrame(std::unique_ptr<DesktopFrame> frame,
+ const DesktopRect& rect);
+
+ CroppedDesktopFrame(const CroppedDesktopFrame&) = delete;
+ CroppedDesktopFrame& operator=(const CroppedDesktopFrame&) = delete;
+
+ private:
+ const std::unique_ptr<DesktopFrame> frame_;
+};
+
+std::unique_ptr<DesktopFrame> CreateCroppedDesktopFrame(
+ std::unique_ptr<DesktopFrame> frame,
+ const DesktopRect& rect) {
+ RTC_DCHECK(frame);
+
+ DesktopRect intersection = DesktopRect::MakeSize(frame->size());
+ intersection.IntersectWith(rect);
+ if (intersection.is_empty()) {
+ return nullptr;
+ }
+
+ if (frame->size().equals(rect.size())) {
+ return frame;
+ }
+
+ return std::unique_ptr<DesktopFrame>(
+ new CroppedDesktopFrame(std::move(frame), intersection));
+}
+
+CroppedDesktopFrame::CroppedDesktopFrame(std::unique_ptr<DesktopFrame> frame,
+ const DesktopRect& rect)
+ : DesktopFrame(rect.size(),
+ frame->stride(),
+ frame->GetFrameDataAtPos(rect.top_left()),
+ frame->shared_memory()),
+ frame_(std::move(frame)) {
+ MoveFrameInfoFrom(frame_.get());
+ set_top_left(frame_->top_left().add(rect.top_left()));
+ mutable_updated_region()->IntersectWith(rect);
+ mutable_updated_region()->Translate(-rect.left(), -rect.top());
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/cropped_desktop_frame.h b/third_party/libwebrtc/modules/desktop_capture/cropped_desktop_frame.h
new file mode 100644
index 0000000000..5c672c7d32
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/cropped_desktop_frame.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_CROPPED_DESKTOP_FRAME_H_
+#define MODULES_DESKTOP_CAPTURE_CROPPED_DESKTOP_FRAME_H_
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// Creates a DesktopFrame to contain only the area of `rect` in the original
+// `frame`.
+// `frame` should not be nullptr. `rect` is in `frame` coordinate, i.e.
+// `frame`->top_left() does not impact the area of `rect`.
+// Returns nullptr frame if `rect` is not contained by the bounds of `frame`.
+std::unique_ptr<DesktopFrame> RTC_EXPORT
+CreateCroppedDesktopFrame(std::unique_ptr<DesktopFrame> frame,
+ const DesktopRect& rect);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_CROPPED_DESKTOP_FRAME_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/cropped_desktop_frame_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/cropped_desktop_frame_unittest.cc
new file mode 100644
index 0000000000..9becf69636
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/cropped_desktop_frame_unittest.cc
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/cropped_desktop_frame.h"
+
+#include <memory>
+#include <utility>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+std::unique_ptr<DesktopFrame> CreateTestFrame() {
+ return std::make_unique<BasicDesktopFrame>(DesktopSize(10, 20));
+}
+
+TEST(CroppedDesktopFrameTest, DoNotCreateWrapperIfSizeIsNotChanged) {
+ std::unique_ptr<DesktopFrame> original = CreateTestFrame();
+ // owned by `original` and CroppedDesktopFrame.
+ DesktopFrame* raw_original = original.get();
+ std::unique_ptr<DesktopFrame> cropped = CreateCroppedDesktopFrame(
+ std::move(original), DesktopRect::MakeWH(10, 20));
+ ASSERT_EQ(cropped.get(), raw_original);
+}
+
+TEST(CroppedDesktopFrameTest, CropWhenPartiallyOutOfBounds) {
+ std::unique_ptr<DesktopFrame> cropped =
+ CreateCroppedDesktopFrame(CreateTestFrame(), DesktopRect::MakeWH(11, 10));
+ ASSERT_NE(nullptr, cropped);
+ ASSERT_EQ(cropped->size().width(), 10);
+ ASSERT_EQ(cropped->size().height(), 10);
+ ASSERT_EQ(cropped->top_left().x(), 0);
+ ASSERT_EQ(cropped->top_left().y(), 0);
+}
+
+TEST(CroppedDesktopFrameTest, ReturnNullIfCropRegionIsOutOfBounds) {
+ std::unique_ptr<DesktopFrame> frame = CreateTestFrame();
+ frame->set_top_left(DesktopVector(100, 200));
+ ASSERT_EQ(nullptr,
+ CreateCroppedDesktopFrame(
+ std::move(frame), DesktopRect::MakeLTRB(101, 203, 109, 218)));
+}
+
+TEST(CroppedDesktopFrameTest, CropASubArea) {
+ std::unique_ptr<DesktopFrame> cropped = CreateCroppedDesktopFrame(
+ CreateTestFrame(), DesktopRect::MakeLTRB(1, 2, 9, 19));
+ ASSERT_EQ(cropped->size().width(), 8);
+ ASSERT_EQ(cropped->size().height(), 17);
+ ASSERT_EQ(cropped->top_left().x(), 1);
+ ASSERT_EQ(cropped->top_left().y(), 2);
+}
+
+TEST(CroppedDesktopFrameTest, SetTopLeft) {
+ std::unique_ptr<DesktopFrame> frame = CreateTestFrame();
+ frame->set_top_left(DesktopVector(100, 200));
+ frame = CreateCroppedDesktopFrame(std::move(frame),
+ DesktopRect::MakeLTRB(1, 3, 9, 18));
+ ASSERT_EQ(frame->size().width(), 8);
+ ASSERT_EQ(frame->size().height(), 15);
+ ASSERT_EQ(frame->top_left().x(), 101);
+ ASSERT_EQ(frame->top_left().y(), 203);
+}
+
+TEST(CroppedDesktopFrameTest, InitializedWithZeros) {
+ std::unique_ptr<DesktopFrame> frame = CreateTestFrame();
+ const DesktopVector frame_origin = frame->top_left();
+ const DesktopSize frame_size = frame->size();
+ std::unique_ptr<DesktopFrame> cropped = CreateCroppedDesktopFrame(
+ std::move(frame), DesktopRect::MakeOriginSize(frame_origin, frame_size));
+ for (int j = 0; j < cropped->size().height(); ++j) {
+ for (int i = 0; i < cropped->stride(); ++i) {
+ ASSERT_EQ(cropped->data()[i + j * cropped->stride()], 0);
+ }
+ }
+}
+
+TEST(CroppedDesktopFrameTest, IccProfile) {
+ const uint8_t fake_icc_profile_data_array[] = {0x1a, 0x00, 0x2b, 0x00,
+ 0x3c, 0x00, 0x4d};
+ const std::vector<uint8_t> icc_profile(
+ fake_icc_profile_data_array,
+ fake_icc_profile_data_array + sizeof(fake_icc_profile_data_array));
+
+ std::unique_ptr<DesktopFrame> frame = CreateTestFrame();
+ EXPECT_EQ(frame->icc_profile().size(), 0UL);
+
+ frame->set_icc_profile(icc_profile);
+ EXPECT_EQ(frame->icc_profile().size(), 7UL);
+ EXPECT_EQ(frame->icc_profile(), icc_profile);
+
+ frame = CreateCroppedDesktopFrame(std::move(frame),
+ DesktopRect::MakeLTRB(2, 2, 8, 18));
+ EXPECT_EQ(frame->icc_profile().size(), 7UL);
+ EXPECT_EQ(frame->icc_profile(), icc_profile);
+
+ std::unique_ptr<SharedDesktopFrame> shared =
+ SharedDesktopFrame::Wrap(std::move(frame));
+ EXPECT_EQ(shared->icc_profile().size(), 7UL);
+ EXPECT_EQ(shared->icc_profile(), icc_profile);
+
+ std::unique_ptr<DesktopFrame> shared_other = shared->Share();
+ EXPECT_EQ(shared_other->icc_profile().size(), 7UL);
+ EXPECT_EQ(shared_other->icc_profile(), icc_profile);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/cropping_window_capturer.cc b/third_party/libwebrtc/modules/desktop_capture/cropping_window_capturer.cc
new file mode 100644
index 0000000000..5e0faaade9
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/cropping_window_capturer.cc
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/cropping_window_capturer.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "modules/desktop_capture/cropped_desktop_frame.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+CroppingWindowCapturer::CroppingWindowCapturer(
+ const DesktopCaptureOptions& options)
+ : options_(options),
+ callback_(NULL),
+ window_capturer_(DesktopCapturer::CreateRawWindowCapturer(options)),
+ selected_window_(kNullWindowId),
+ excluded_window_(kNullWindowId) {}
+
+CroppingWindowCapturer::~CroppingWindowCapturer() {}
+
+void CroppingWindowCapturer::Start(DesktopCapturer::Callback* callback) {
+ callback_ = callback;
+ window_capturer_->Start(callback);
+}
+
+void CroppingWindowCapturer::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
+ window_capturer_->SetSharedMemoryFactory(std::move(shared_memory_factory));
+}
+
+void CroppingWindowCapturer::CaptureFrame() {
+ if (ShouldUseScreenCapturer()) {
+ if (!screen_capturer_.get()) {
+ screen_capturer_ = DesktopCapturer::CreateRawScreenCapturer(options_);
+ if (excluded_window_) {
+ screen_capturer_->SetExcludedWindow(excluded_window_);
+ }
+ screen_capturer_->Start(this);
+ }
+ screen_capturer_->CaptureFrame();
+ } else {
+ window_capturer_->CaptureFrame();
+ }
+}
+
+void CroppingWindowCapturer::SetExcludedWindow(WindowId window) {
+ excluded_window_ = window;
+ if (screen_capturer_.get()) {
+ screen_capturer_->SetExcludedWindow(window);
+ }
+}
+
+bool CroppingWindowCapturer::GetSourceList(SourceList* sources) {
+ return window_capturer_->GetSourceList(sources);
+}
+
+bool CroppingWindowCapturer::SelectSource(SourceId id) {
+ if (window_capturer_->SelectSource(id)) {
+ selected_window_ = id;
+ return true;
+ }
+ return false;
+}
+
+bool CroppingWindowCapturer::FocusOnSelectedSource() {
+ return window_capturer_->FocusOnSelectedSource();
+}
+
+void CroppingWindowCapturer::OnCaptureResult(
+ DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame> screen_frame) {
+ if (!ShouldUseScreenCapturer()) {
+ RTC_LOG(LS_INFO) << "Window no longer on top when ScreenCapturer finishes";
+ window_capturer_->CaptureFrame();
+ return;
+ }
+
+ if (result != Result::SUCCESS) {
+ RTC_LOG(LS_WARNING) << "ScreenCapturer failed to capture a frame";
+ callback_->OnCaptureResult(result, nullptr);
+ return;
+ }
+
+ DesktopRect window_rect = GetWindowRectInVirtualScreen();
+ if (window_rect.is_empty()) {
+ RTC_LOG(LS_WARNING) << "Window rect is empty";
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+
+ std::unique_ptr<DesktopFrame> cropped_frame =
+ CreateCroppedDesktopFrame(std::move(screen_frame), window_rect);
+
+ if (!cropped_frame) {
+ RTC_LOG(LS_WARNING) << "Window is outside of the captured display";
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(cropped_frame));
+}
+
+bool CroppingWindowCapturer::IsOccluded(const DesktopVector& pos) {
+ // Returns true if either capturer returns true.
+ if (window_capturer_->IsOccluded(pos)) {
+ return true;
+ }
+ if (screen_capturer_ != nullptr && screen_capturer_->IsOccluded(pos)) {
+ return true;
+ }
+ return false;
+}
+
+#if !defined(WEBRTC_WIN)
+// CroppingWindowCapturer is implemented only for windows. On other platforms
+// the regular window capturer is used.
+// static
+std::unique_ptr<DesktopCapturer> CroppingWindowCapturer::CreateCapturer(
+ const DesktopCaptureOptions& options) {
+ return DesktopCapturer::CreateWindowCapturer(options);
+}
+#endif
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/cropping_window_capturer.h b/third_party/libwebrtc/modules/desktop_capture/cropping_window_capturer.h
new file mode 100644
index 0000000000..56478030b1
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/cropping_window_capturer.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_CROPPING_WINDOW_CAPTURER_H_
+#define MODULES_DESKTOP_CAPTURE_CROPPING_WINDOW_CAPTURER_H_
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/shared_memory.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// WindowCapturer implementation that uses a screen capturer to capture the
+// whole screen and crops the video frame to the window area when the captured
+// window is on top.
+class RTC_EXPORT CroppingWindowCapturer : public DesktopCapturer,
+ public DesktopCapturer::Callback {
+ public:
+ static std::unique_ptr<DesktopCapturer> CreateCapturer(
+ const DesktopCaptureOptions& options);
+
+ ~CroppingWindowCapturer() override;
+
+ // DesktopCapturer implementation.
+ void Start(DesktopCapturer::Callback* callback) override;
+ void SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) override;
+ void CaptureFrame() override;
+ void SetExcludedWindow(WindowId window) override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+ bool FocusOnSelectedSource() override;
+ bool IsOccluded(const DesktopVector& pos) override;
+
+ // DesktopCapturer::Callback implementation, passed to `screen_capturer_` to
+ // intercept the capture result.
+ void OnCaptureResult(DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame> frame) override;
+
+ protected:
+ explicit CroppingWindowCapturer(const DesktopCaptureOptions& options);
+
+ // The platform implementation should override these methods.
+
+ // Returns true if it is OK to capture the whole screen and crop to the
+ // selected window, i.e. the selected window is opaque, rectangular, and not
+ // occluded.
+ virtual bool ShouldUseScreenCapturer() = 0;
+
+ // Returns the window area relative to the top left of the virtual screen
+ // within the bounds of the virtual screen. This function should return the
+ // DesktopRect in full desktop coordinates, i.e. the top-left monitor starts
+ // from (0, 0).
+ virtual DesktopRect GetWindowRectInVirtualScreen() = 0;
+
+ WindowId selected_window() const { return selected_window_; }
+ WindowId excluded_window() const { return excluded_window_; }
+ DesktopCapturer* window_capturer() const { return window_capturer_.get(); }
+
+ private:
+ DesktopCaptureOptions options_;
+ DesktopCapturer::Callback* callback_;
+ std::unique_ptr<DesktopCapturer> window_capturer_;
+ std::unique_ptr<DesktopCapturer> screen_capturer_;
+ SourceId selected_window_;
+ WindowId excluded_window_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_CROPPING_WINDOW_CAPTURER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/cropping_window_capturer_win.cc b/third_party/libwebrtc/modules/desktop_capture/cropping_window_capturer_win.cc
new file mode 100644
index 0000000000..64d9219e24
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/cropping_window_capturer_win.cc
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/cropping_window_capturer.h"
+#include "modules/desktop_capture/desktop_capturer_differ_wrapper.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "modules/desktop_capture/win/selected_window_context.h"
+#include "modules/desktop_capture/win/window_capture_utils.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/trace_event.h"
+#include "rtc_base/win/windows_version.h"
+
+namespace webrtc {
+
+namespace {
+
+// Used to pass input data for verifying the selected window is on top.
+struct TopWindowVerifierContext : public SelectedWindowContext {
+ TopWindowVerifierContext(HWND selected_window,
+ HWND excluded_window,
+ DesktopRect selected_window_rect,
+ WindowCaptureHelperWin* window_capture_helper)
+ : SelectedWindowContext(selected_window,
+ selected_window_rect,
+ window_capture_helper),
+ excluded_window(excluded_window) {
+ RTC_DCHECK_NE(selected_window, excluded_window);
+ }
+
+ // Determines whether the selected window is on top (not occluded by any
+ // windows except for those it owns or any excluded window).
+ bool IsTopWindow() {
+ if (!IsSelectedWindowValid()) {
+ return false;
+ }
+
+ // Enumerate all top-level windows above the selected window in Z-order,
+ // checking whether any overlaps it. This uses FindWindowEx rather than
+ // EnumWindows because the latter excludes certain system windows (e.g. the
+ // Start menu & other taskbar menus) that should be detected here to avoid
+ // inadvertent capture.
+ int num_retries = 0;
+ while (true) {
+ HWND hwnd = nullptr;
+ while ((hwnd = FindWindowEx(nullptr, hwnd, nullptr, nullptr))) {
+ if (hwnd == selected_window()) {
+ // Windows are enumerated in top-down Z-order, so we can stop
+ // enumerating upon reaching the selected window & report it's on top.
+ return true;
+ }
+
+ // Ignore the excluded window.
+ if (hwnd == excluded_window) {
+ continue;
+ }
+
+ // Ignore windows that aren't visible on the current desktop.
+ if (!window_capture_helper()->IsWindowVisibleOnCurrentDesktop(hwnd)) {
+ continue;
+ }
+
+ // Ignore Chrome notification windows, especially the notification for
+ // the ongoing window sharing. Notes:
+ // - This only works with notifications from Chrome, not other Apps.
+ // - All notifications from Chrome will be ignored.
+ // - This may cause part or whole of notification window being cropped
+ // into the capturing of the target window if there is overlapping.
+ if (window_capture_helper()->IsWindowChromeNotification(hwnd)) {
+ continue;
+ }
+
+ // Ignore windows owned by the selected window since we want to capture
+ // them.
+ if (IsWindowOwnedBySelectedWindow(hwnd)) {
+ continue;
+ }
+
+ // Check whether this window intersects with the selected window.
+ if (IsWindowOverlappingSelectedWindow(hwnd)) {
+ // If intersection is not empty, the selected window is not on top.
+ return false;
+ }
+ }
+
+ DWORD lastError = GetLastError();
+ if (lastError == ERROR_SUCCESS) {
+ // The enumeration completed successfully without finding the selected
+ // window (which may have been closed).
+ RTC_LOG(LS_WARNING) << "Failed to find selected window (only expected "
+ "if it was closed)";
+ RTC_DCHECK(!IsWindow(selected_window()));
+ return false;
+ } else if (lastError == ERROR_INVALID_WINDOW_HANDLE) {
+ // This error may occur if a window is closed around the time it's
+ // enumerated; retry the enumeration in this case up to 10 times
+ // (this should be a rare race & unlikely to recur).
+ if (++num_retries <= 10) {
+ RTC_LOG(LS_WARNING) << "Enumeration failed due to race with a window "
+ "closing; retrying - retry #"
+ << num_retries;
+ continue;
+ } else {
+ RTC_LOG(LS_ERROR)
+ << "Exhausted retry allowance around window enumeration failures "
+ "due to races with windows closing";
+ }
+ }
+
+ // The enumeration failed with an unexpected error (or more repeats of
+ // an infrequently-expected error than anticipated). After logging this &
+ // firing an assert when enabled, report that the selected window isn't
+ // topmost to avoid inadvertent capture of other windows.
+ RTC_LOG(LS_ERROR) << "Failed to enumerate windows: " << lastError;
+ RTC_DCHECK_NOTREACHED();
+ return false;
+ }
+ }
+
+ const HWND excluded_window;
+};
+
+class CroppingWindowCapturerWin : public CroppingWindowCapturer {
+ public:
+ explicit CroppingWindowCapturerWin(const DesktopCaptureOptions& options)
+ : CroppingWindowCapturer(options),
+ enumerate_current_process_windows_(
+ options.enumerate_current_process_windows()),
+ full_screen_window_detector_(options.full_screen_window_detector()) {}
+
+ void CaptureFrame() override;
+
+ private:
+ bool ShouldUseScreenCapturer() override;
+ DesktopRect GetWindowRectInVirtualScreen() override;
+
+ // Returns either selected by user sourceId or sourceId provided by
+ // FullScreenWindowDetector
+ WindowId GetWindowToCapture() const;
+
+ // The region from GetWindowRgn in the desktop coordinate if the region is
+ // rectangular, or the rect from GetWindowRect if the region is not set.
+ DesktopRect window_region_rect_;
+
+ WindowCaptureHelperWin window_capture_helper_;
+
+ bool enumerate_current_process_windows_;
+
+ rtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector_;
+};
+
+void CroppingWindowCapturerWin::CaptureFrame() {
+ DesktopCapturer* win_capturer = window_capturer();
+ if (win_capturer) {
+ // Feed the actual list of windows into full screen window detector.
+ if (full_screen_window_detector_) {
+ full_screen_window_detector_->UpdateWindowListIfNeeded(
+ selected_window(), [this](DesktopCapturer::SourceList* sources) {
+ // Get the list of top level windows, including ones with empty
+ // title. win_capturer_->GetSourceList can't be used here
+ // cause it filters out the windows with empty titles and
+ // it uses responsiveness check which could lead to performance
+ // issues.
+ SourceList result;
+ int window_list_flags =
+ enumerate_current_process_windows_
+ ? GetWindowListFlags::kNone
+ : GetWindowListFlags::kIgnoreCurrentProcessWindows;
+
+ if (!webrtc::GetWindowList(window_list_flags, &result))
+ return false;
+
+ // Filter out windows not visible on current desktop
+ auto it = std::remove_if(
+ result.begin(), result.end(), [this](const auto& source) {
+ HWND hwnd = reinterpret_cast<HWND>(source.id);
+ return !window_capture_helper_
+ .IsWindowVisibleOnCurrentDesktop(hwnd);
+ });
+ result.erase(it, result.end());
+
+ sources->swap(result);
+ return true;
+ });
+ }
+ win_capturer->SelectSource(GetWindowToCapture());
+ }
+
+ CroppingWindowCapturer::CaptureFrame();
+}
+
+bool CroppingWindowCapturerWin::ShouldUseScreenCapturer() {
+ if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN8 &&
+ window_capture_helper_.IsAeroEnabled()) {
+ return false;
+ }
+
+ const HWND selected = reinterpret_cast<HWND>(GetWindowToCapture());
+ // Check if the window is visible on current desktop.
+ if (!window_capture_helper_.IsWindowVisibleOnCurrentDesktop(selected)) {
+ return false;
+ }
+
+ // Check if the window is a translucent layered window.
+ const LONG window_ex_style = GetWindowLong(selected, GWL_EXSTYLE);
+ if (window_ex_style & WS_EX_LAYERED) {
+ COLORREF color_ref_key = 0;
+ BYTE alpha = 0;
+ DWORD flags = 0;
+
+ // GetLayeredWindowAttributes fails if the window was setup with
+ // UpdateLayeredWindow. We have no way to know the opacity of the window in
+ // that case. This happens for Stiky Note (crbug/412726).
+ if (!GetLayeredWindowAttributes(selected, &color_ref_key, &alpha, &flags))
+ return false;
+
+ // UpdateLayeredWindow is the only way to set per-pixel alpha and will cause
+ // the previous GetLayeredWindowAttributes to fail. So we only need to check
+ // the window wide color key or alpha.
+ if ((flags & LWA_COLORKEY) || ((flags & LWA_ALPHA) && (alpha < 255))) {
+ return false;
+ }
+ }
+
+ if (!GetWindowRect(selected, &window_region_rect_)) {
+ return false;
+ }
+
+ DesktopRect content_rect;
+ if (!GetWindowContentRect(selected, &content_rect)) {
+ return false;
+ }
+
+ DesktopRect region_rect;
+ // Get the window region and check if it is rectangular.
+ const int region_type =
+ GetWindowRegionTypeWithBoundary(selected, &region_rect);
+
+ // Do not use the screen capturer if the region is empty or not rectangular.
+ if (region_type == COMPLEXREGION || region_type == NULLREGION) {
+ return false;
+ }
+
+ if (region_type == SIMPLEREGION) {
+ // The `region_rect` returned from GetRgnBox() is always in window
+ // coordinate.
+ region_rect.Translate(window_region_rect_.left(),
+ window_region_rect_.top());
+ // MSDN: The window region determines the area *within* the window where the
+ // system permits drawing.
+ // https://msdn.microsoft.com/en-us/library/windows/desktop/dd144950(v=vs.85).aspx.
+ //
+ // `region_rect` should always be inside of `window_region_rect_`. So after
+ // the intersection, `window_region_rect_` == `region_rect`. If so, what's
+ // the point of the intersecting operations? Why cannot we directly retrieve
+ // `window_region_rect_` from GetWindowRegionTypeWithBoundary() function?
+ // TODO(zijiehe): Figure out the purpose of these intersections.
+ window_region_rect_.IntersectWith(region_rect);
+ content_rect.IntersectWith(region_rect);
+ }
+
+ // Check if the client area is out of the screen area. When the window is
+ // maximized, only its client area is visible in the screen, the border will
+ // be hidden. So we are using `content_rect` here.
+ if (!GetFullscreenRect().ContainsRect(content_rect)) {
+ return false;
+ }
+
+ // Check if the window is occluded by any other window, excluding the child
+ // windows, context menus, and `excluded_window_`.
+ // `content_rect` is preferred, see the comments on
+ // IsWindowIntersectWithSelectedWindow().
+ TopWindowVerifierContext context(selected,
+ reinterpret_cast<HWND>(excluded_window()),
+ content_rect, &window_capture_helper_);
+ return context.IsTopWindow();
+}
+
+DesktopRect CroppingWindowCapturerWin::GetWindowRectInVirtualScreen() {
+ TRACE_EVENT0("webrtc",
+ "CroppingWindowCapturerWin::GetWindowRectInVirtualScreen");
+ DesktopRect window_rect;
+ HWND hwnd = reinterpret_cast<HWND>(GetWindowToCapture());
+ if (!GetCroppedWindowRect(hwnd, /*avoid_cropping_border*/ false, &window_rect,
+ /*original_rect*/ nullptr)) {
+ RTC_LOG(LS_WARNING) << "Failed to get window info: " << GetLastError();
+ return window_rect;
+ }
+ window_rect.IntersectWith(window_region_rect_);
+
+ // Convert `window_rect` to be relative to the top-left of the virtual screen.
+ DesktopRect screen_rect(GetFullscreenRect());
+ window_rect.IntersectWith(screen_rect);
+ window_rect.Translate(-screen_rect.left(), -screen_rect.top());
+ return window_rect;
+}
+
+WindowId CroppingWindowCapturerWin::GetWindowToCapture() const {
+ const auto selected_source = selected_window();
+ const auto full_screen_source =
+ full_screen_window_detector_
+ ? full_screen_window_detector_->FindFullScreenWindow(selected_source)
+ : 0;
+ return full_screen_source ? full_screen_source : selected_source;
+}
+
+} // namespace
+
+// static
+std::unique_ptr<DesktopCapturer> CroppingWindowCapturer::CreateCapturer(
+ const DesktopCaptureOptions& options) {
+ std::unique_ptr<DesktopCapturer> capturer(
+ new CroppingWindowCapturerWin(options));
+ if (capturer && options.detect_updated_region()) {
+ capturer.reset(new DesktopCapturerDifferWrapper(std::move(capturer)));
+ }
+
+ return capturer;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_and_cursor_composer.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_and_cursor_composer.cc
new file mode 100644
index 0000000000..dd688ac5f2
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_and_cursor_composer.cc
@@ -0,0 +1,260 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_and_cursor_composer.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include <memory>
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/mouse_cursor_monitor.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+namespace {
+
+// Helper function that blends one image into another. Source image must be
+// pre-multiplied with the alpha channel. Destination is assumed to be opaque.
+void AlphaBlend(uint8_t* dest,
+ int dest_stride,
+ const uint8_t* src,
+ int src_stride,
+ const DesktopSize& size) {
+ for (int y = 0; y < size.height(); ++y) {
+ for (int x = 0; x < size.width(); ++x) {
+ uint32_t base_alpha = 255 - src[x * DesktopFrame::kBytesPerPixel + 3];
+ if (base_alpha == 255) {
+ continue;
+ } else if (base_alpha == 0) {
+ memcpy(dest + x * DesktopFrame::kBytesPerPixel,
+ src + x * DesktopFrame::kBytesPerPixel,
+ DesktopFrame::kBytesPerPixel);
+ } else {
+ dest[x * DesktopFrame::kBytesPerPixel] =
+ dest[x * DesktopFrame::kBytesPerPixel] * base_alpha / 255 +
+ src[x * DesktopFrame::kBytesPerPixel];
+ dest[x * DesktopFrame::kBytesPerPixel + 1] =
+ dest[x * DesktopFrame::kBytesPerPixel + 1] * base_alpha / 255 +
+ src[x * DesktopFrame::kBytesPerPixel + 1];
+ dest[x * DesktopFrame::kBytesPerPixel + 2] =
+ dest[x * DesktopFrame::kBytesPerPixel + 2] * base_alpha / 255 +
+ src[x * DesktopFrame::kBytesPerPixel + 2];
+ }
+ }
+ src += src_stride;
+ dest += dest_stride;
+ }
+}
+
+// DesktopFrame wrapper that draws mouse on a frame and restores original
+// content before releasing the underlying frame.
+class DesktopFrameWithCursor : public DesktopFrame {
+ public:
+ // Takes ownership of `frame`.
+ DesktopFrameWithCursor(std::unique_ptr<DesktopFrame> frame,
+ const MouseCursor& cursor,
+ const DesktopVector& position,
+ const DesktopRect& previous_cursor_rect,
+ bool cursor_changed);
+ ~DesktopFrameWithCursor() override;
+
+ DesktopFrameWithCursor(const DesktopFrameWithCursor&) = delete;
+ DesktopFrameWithCursor& operator=(const DesktopFrameWithCursor&) = delete;
+
+ DesktopRect cursor_rect() const { return cursor_rect_; }
+
+ private:
+ const std::unique_ptr<DesktopFrame> original_frame_;
+
+ DesktopVector restore_position_;
+ std::unique_ptr<DesktopFrame> restore_frame_;
+ DesktopRect cursor_rect_;
+};
+
+DesktopFrameWithCursor::DesktopFrameWithCursor(
+ std::unique_ptr<DesktopFrame> frame,
+ const MouseCursor& cursor,
+ const DesktopVector& position,
+ const DesktopRect& previous_cursor_rect,
+ bool cursor_changed)
+ : DesktopFrame(frame->size(),
+ frame->stride(),
+ frame->data(),
+ frame->shared_memory()),
+ original_frame_(std::move(frame)) {
+ MoveFrameInfoFrom(original_frame_.get());
+
+ DesktopVector image_pos = position.subtract(cursor.hotspot());
+ cursor_rect_ = DesktopRect::MakeSize(cursor.image()->size());
+ cursor_rect_.Translate(image_pos);
+ DesktopVector cursor_origin = cursor_rect_.top_left();
+ cursor_rect_.IntersectWith(DesktopRect::MakeSize(size()));
+
+ if (!previous_cursor_rect.equals(cursor_rect_)) {
+ mutable_updated_region()->AddRect(cursor_rect_);
+ // TODO(crbug:1323241) Update this code to properly handle the case where
+ // |previous_cursor_rect| is outside of the boundaries of |frame|.
+ // Any boundary check has to take into account the fact that
+ // |previous_cursor_rect| can be in DPI or in pixels, based on the platform
+ // we're running on.
+ mutable_updated_region()->AddRect(previous_cursor_rect);
+ } else if (cursor_changed) {
+ mutable_updated_region()->AddRect(cursor_rect_);
+ }
+
+ if (cursor_rect_.is_empty())
+ return;
+
+ // Copy original screen content under cursor to `restore_frame_`.
+ restore_position_ = cursor_rect_.top_left();
+ restore_frame_.reset(new BasicDesktopFrame(cursor_rect_.size()));
+ restore_frame_->CopyPixelsFrom(*this, cursor_rect_.top_left(),
+ DesktopRect::MakeSize(restore_frame_->size()));
+
+ // Blit the cursor.
+ uint8_t* cursor_rect_data =
+ reinterpret_cast<uint8_t*>(data()) + cursor_rect_.top() * stride() +
+ cursor_rect_.left() * DesktopFrame::kBytesPerPixel;
+ DesktopVector origin_shift = cursor_rect_.top_left().subtract(cursor_origin);
+ AlphaBlend(cursor_rect_data, stride(),
+ cursor.image()->data() +
+ origin_shift.y() * cursor.image()->stride() +
+ origin_shift.x() * DesktopFrame::kBytesPerPixel,
+ cursor.image()->stride(), cursor_rect_.size());
+}
+
+DesktopFrameWithCursor::~DesktopFrameWithCursor() {
+ // Restore original content of the frame.
+ if (restore_frame_) {
+ DesktopRect target_rect = DesktopRect::MakeSize(restore_frame_->size());
+ target_rect.Translate(restore_position_);
+ CopyPixelsFrom(restore_frame_->data(), restore_frame_->stride(),
+ target_rect);
+ }
+}
+
+} // namespace
+
+DesktopAndCursorComposer::DesktopAndCursorComposer(
+ std::unique_ptr<DesktopCapturer> desktop_capturer,
+ const DesktopCaptureOptions& options)
+ : DesktopAndCursorComposer(desktop_capturer.release(),
+ MouseCursorMonitor::Create(options).release()) {}
+
+DesktopAndCursorComposer::DesktopAndCursorComposer(
+ DesktopCapturer* desktop_capturer,
+ MouseCursorMonitor* mouse_monitor)
+ : desktop_capturer_(desktop_capturer), mouse_monitor_(mouse_monitor) {
+ RTC_DCHECK(desktop_capturer_);
+}
+
+DesktopAndCursorComposer::~DesktopAndCursorComposer() = default;
+
+std::unique_ptr<DesktopAndCursorComposer>
+DesktopAndCursorComposer::CreateWithoutMouseCursorMonitor(
+ std::unique_ptr<DesktopCapturer> desktop_capturer) {
+ return std::unique_ptr<DesktopAndCursorComposer>(
+ new DesktopAndCursorComposer(desktop_capturer.release(), nullptr));
+}
+
+void DesktopAndCursorComposer::Start(DesktopCapturer::Callback* callback) {
+ callback_ = callback;
+ if (mouse_monitor_)
+ mouse_monitor_->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION);
+ desktop_capturer_->Start(this);
+}
+
+void DesktopAndCursorComposer::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
+ desktop_capturer_->SetSharedMemoryFactory(std::move(shared_memory_factory));
+}
+
+void DesktopAndCursorComposer::CaptureFrame() {
+ if (mouse_monitor_)
+ mouse_monitor_->Capture();
+ desktop_capturer_->CaptureFrame();
+}
+
+void DesktopAndCursorComposer::SetExcludedWindow(WindowId window) {
+ desktop_capturer_->SetExcludedWindow(window);
+}
+
+bool DesktopAndCursorComposer::GetSourceList(SourceList* sources) {
+ return desktop_capturer_->GetSourceList(sources);
+}
+
+bool DesktopAndCursorComposer::SelectSource(SourceId id) {
+ return desktop_capturer_->SelectSource(id);
+}
+
+bool DesktopAndCursorComposer::FocusOnSelectedSource() {
+ return desktop_capturer_->FocusOnSelectedSource();
+}
+
+bool DesktopAndCursorComposer::IsOccluded(const DesktopVector& pos) {
+ return desktop_capturer_->IsOccluded(pos);
+}
+
+#if defined(WEBRTC_USE_GIO)
+DesktopCaptureMetadata DesktopAndCursorComposer::GetMetadata() {
+ return desktop_capturer_->GetMetadata();
+}
+#endif // defined(WEBRTC_USE_GIO)
+
+void DesktopAndCursorComposer::OnCaptureResult(
+ DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame> frame) {
+ if (frame && cursor_) {
+ if (!frame->may_contain_cursor() &&
+ frame->rect().Contains(cursor_position_) &&
+ !desktop_capturer_->IsOccluded(cursor_position_)) {
+ DesktopVector relative_position =
+ cursor_position_.subtract(frame->top_left());
+#if defined(WEBRTC_MAC) || defined(CHROMEOS)
+ // On OSX, the logical(DIP) and physical coordinates are used mixingly.
+ // For example, the captured cursor has its size in physical pixels(2x)
+ // and location in logical(DIP) pixels on Retina monitor. This will cause
+ // problem when the desktop is mixed with Retina and non-Retina monitors.
+ // So we use DIP pixel for all location info and compensate with the scale
+ // factor of current frame to the `relative_position`.
+ const float scale = frame->scale_factor();
+ relative_position.set(relative_position.x() * scale,
+ relative_position.y() * scale);
+#endif
+ auto frame_with_cursor = std::make_unique<DesktopFrameWithCursor>(
+ std::move(frame), *cursor_, relative_position, previous_cursor_rect_,
+ cursor_changed_);
+ previous_cursor_rect_ = frame_with_cursor->cursor_rect();
+ cursor_changed_ = false;
+ frame = std::move(frame_with_cursor);
+ frame->set_may_contain_cursor(true);
+ }
+ }
+
+ callback_->OnCaptureResult(result, std::move(frame));
+}
+
+void DesktopAndCursorComposer::OnMouseCursor(MouseCursor* cursor) {
+ cursor_changed_ = true;
+ cursor_.reset(cursor);
+}
+
+void DesktopAndCursorComposer::OnMouseCursorPosition(
+ const DesktopVector& position) {
+ cursor_position_ = position;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_and_cursor_composer.h b/third_party/libwebrtc/modules/desktop_capture/desktop_and_cursor_composer.h
new file mode 100644
index 0000000000..a078b3eeef
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_and_cursor_composer.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_DESKTOP_AND_CURSOR_COMPOSER_H_
+#define MODULES_DESKTOP_CAPTURE_DESKTOP_AND_CURSOR_COMPOSER_H_
+
+#include <memory>
+#if defined(WEBRTC_USE_GIO)
+#include "modules/desktop_capture/desktop_capture_metadata.h"
+#endif // defined(WEBRTC_USE_GIO)
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/mouse_cursor_monitor.h"
+#include "modules/desktop_capture/shared_memory.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// A wrapper for DesktopCapturer that also captures mouse using specified
+// MouseCursorMonitor and renders it on the generated streams.
+class RTC_EXPORT DesktopAndCursorComposer
+ : public DesktopCapturer,
+ public DesktopCapturer::Callback,
+ public MouseCursorMonitor::Callback {
+ public:
+ // Creates a new composer that captures mouse cursor using
+ // MouseCursorMonitor::Create(options) and renders it into the frames
+ // generated by `desktop_capturer`.
+ DesktopAndCursorComposer(std::unique_ptr<DesktopCapturer> desktop_capturer,
+ const DesktopCaptureOptions& options);
+
+ ~DesktopAndCursorComposer() override;
+
+ DesktopAndCursorComposer(const DesktopAndCursorComposer&) = delete;
+ DesktopAndCursorComposer& operator=(const DesktopAndCursorComposer&) = delete;
+
+ // Creates a new composer that relies on an external source for cursor shape
+ // and position information via the MouseCursorMonitor::Callback interface.
+ static std::unique_ptr<DesktopAndCursorComposer>
+ CreateWithoutMouseCursorMonitor(
+ std::unique_ptr<DesktopCapturer> desktop_capturer);
+
+ // DesktopCapturer interface.
+ void Start(DesktopCapturer::Callback* callback) override;
+ void SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) override;
+ void CaptureFrame() override;
+ void SetExcludedWindow(WindowId window) override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+ bool FocusOnSelectedSource() override;
+ bool IsOccluded(const DesktopVector& pos) override;
+#if defined(WEBRTC_USE_GIO)
+ DesktopCaptureMetadata GetMetadata() override;
+#endif // defined(WEBRTC_USE_GIO)
+
+ // MouseCursorMonitor::Callback interface.
+ void OnMouseCursor(MouseCursor* cursor) override;
+ void OnMouseCursorPosition(const DesktopVector& position) override;
+
+ private:
+ // Allows test cases to use a fake MouseCursorMonitor implementation.
+ friend class DesktopAndCursorComposerTest;
+
+ // Constructor to delegate both deprecated and new constructors and allows
+ // test cases to use a fake MouseCursorMonitor implementation.
+ DesktopAndCursorComposer(DesktopCapturer* desktop_capturer,
+ MouseCursorMonitor* mouse_monitor);
+
+ // DesktopCapturer::Callback interface.
+ void OnCaptureResult(DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame> frame) override;
+
+ const std::unique_ptr<DesktopCapturer> desktop_capturer_;
+ const std::unique_ptr<MouseCursorMonitor> mouse_monitor_;
+
+ DesktopCapturer::Callback* callback_;
+
+ std::unique_ptr<MouseCursor> cursor_;
+ DesktopVector cursor_position_;
+ DesktopRect previous_cursor_rect_;
+ bool cursor_changed_ = false;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_AND_CURSOR_COMPOSER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc
new file mode 100644
index 0000000000..c26dc208ac
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_and_cursor_composer_unittest.cc
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_and_cursor_composer.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "rtc_base/arraysize.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+using testing::ElementsAre;
+
+const int kFrameXCoord = 100;
+const int kFrameYCoord = 200;
+const int kScreenWidth = 100;
+const int kScreenHeight = 100;
+const int kCursorWidth = 10;
+const int kCursorHeight = 10;
+
+const int kTestCursorSize = 3;
+const uint32_t kTestCursorData[kTestCursorSize][kTestCursorSize] = {
+ {
+ 0xffffffff,
+ 0x99990000,
+ 0xaa222222,
+ },
+ {
+ 0x88008800,
+ 0xaa0000aa,
+ 0xaa333333,
+ },
+ {
+ 0x00000000,
+ 0xaa0000aa,
+ 0xaa333333,
+ },
+};
+
+uint32_t GetFakeFramePixelValue(const DesktopVector& p) {
+ uint32_t r = 100 + p.x();
+ uint32_t g = 100 + p.y();
+ uint32_t b = 100 + p.x() + p.y();
+ return b + (g << 8) + (r << 16) + 0xff000000;
+}
+
+uint32_t GetFramePixel(const DesktopFrame& frame, const DesktopVector& pos) {
+ return *reinterpret_cast<uint32_t*>(frame.GetFrameDataAtPos(pos));
+}
+
+// Blends two pixel values taking into account alpha.
+uint32_t BlendPixels(uint32_t dest, uint32_t src) {
+ uint8_t alpha = 255 - ((src & 0xff000000) >> 24);
+ uint32_t r =
+ ((dest & 0x00ff0000) >> 16) * alpha / 255 + ((src & 0x00ff0000) >> 16);
+ uint32_t g =
+ ((dest & 0x0000ff00) >> 8) * alpha / 255 + ((src & 0x0000ff00) >> 8);
+ uint32_t b = (dest & 0x000000ff) * alpha / 255 + (src & 0x000000ff);
+ return b + (g << 8) + (r << 16) + 0xff000000;
+}
+
+DesktopFrame* CreateTestFrame(int width = kScreenWidth,
+ int height = kScreenHeight) {
+ DesktopFrame* frame = new BasicDesktopFrame(DesktopSize(width, height));
+ uint32_t* data = reinterpret_cast<uint32_t*>(frame->data());
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ *(data++) = GetFakeFramePixelValue(DesktopVector(x, y));
+ }
+ }
+ return frame;
+}
+
+MouseCursor* CreateTestCursor(DesktopVector hotspot) {
+ std::unique_ptr<DesktopFrame> image(
+ new BasicDesktopFrame(DesktopSize(kCursorWidth, kCursorHeight)));
+ uint32_t* data = reinterpret_cast<uint32_t*>(image->data());
+ // Set four pixels near the hotspot and leave all other blank.
+ for (int y = 0; y < kTestCursorSize; ++y) {
+ for (int x = 0; x < kTestCursorSize; ++x) {
+ data[(hotspot.y() + y) * kCursorWidth + (hotspot.x() + x)] =
+ kTestCursorData[y][x];
+ }
+ }
+ return new MouseCursor(image.release(), hotspot);
+}
+
+class FakeScreenCapturer : public DesktopCapturer {
+ public:
+ FakeScreenCapturer() {}
+
+ void Start(Callback* callback) override { callback_ = callback; }
+
+ void CaptureFrame() override {
+ callback_->OnCaptureResult(
+ next_frame_ ? Result::SUCCESS : Result::ERROR_TEMPORARY,
+ std::move(next_frame_));
+ }
+
+ void SetNextFrame(std::unique_ptr<DesktopFrame> next_frame) {
+ next_frame_ = std::move(next_frame);
+ }
+
+ bool IsOccluded(const DesktopVector& pos) override { return is_occluded_; }
+
+ void set_is_occluded(bool value) { is_occluded_ = value; }
+
+ private:
+ Callback* callback_ = nullptr;
+
+ std::unique_ptr<DesktopFrame> next_frame_;
+ bool is_occluded_ = false;
+};
+
+class FakeMouseMonitor : public MouseCursorMonitor {
+ public:
+ FakeMouseMonitor() : changed_(true) {}
+
+ void SetState(CursorState state, const DesktopVector& pos) {
+ state_ = state;
+ position_ = pos;
+ }
+
+ void SetHotspot(const DesktopVector& hotspot) {
+ if (!hotspot_.equals(hotspot))
+ changed_ = true;
+ hotspot_ = hotspot;
+ }
+
+ void Init(Callback* callback, Mode mode) { callback_ = callback; }
+
+ void Capture() override {
+ if (changed_) {
+ callback_->OnMouseCursor(CreateTestCursor(hotspot_));
+ }
+ callback_->OnMouseCursorPosition(position_);
+ }
+
+ private:
+ Callback* callback_;
+ CursorState state_;
+ DesktopVector position_;
+ DesktopVector hotspot_;
+ bool changed_;
+};
+
+void VerifyFrame(const DesktopFrame& frame,
+ MouseCursorMonitor::CursorState state,
+ const DesktopVector& pos) {
+ // Verify that all other pixels are set to their original values.
+ DesktopRect image_rect =
+ DesktopRect::MakeWH(kTestCursorSize, kTestCursorSize);
+ image_rect.Translate(pos);
+
+ for (int y = 0; y < kScreenHeight; ++y) {
+ for (int x = 0; x < kScreenWidth; ++x) {
+ DesktopVector p(x, y);
+ if (state == MouseCursorMonitor::INSIDE && image_rect.Contains(p)) {
+ EXPECT_EQ(BlendPixels(GetFakeFramePixelValue(p),
+ kTestCursorData[y - pos.y()][x - pos.x()]),
+ GetFramePixel(frame, p));
+ } else {
+ EXPECT_EQ(GetFakeFramePixelValue(p), GetFramePixel(frame, p));
+ }
+ }
+ }
+}
+
+} // namespace
+
+bool operator==(const DesktopRect& left, const DesktopRect& right) {
+ return left.equals(right);
+}
+
+std::ostream& operator<<(std::ostream& out, const DesktopRect& rect) {
+ out << "{" << rect.left() << "+" << rect.top() << "-" << rect.width() << "x"
+ << rect.height() << "}";
+ return out;
+}
+
+class DesktopAndCursorComposerTest : public ::testing::Test,
+ public DesktopCapturer::Callback {
+ public:
+ explicit DesktopAndCursorComposerTest(bool include_cursor = true)
+ : fake_screen_(new FakeScreenCapturer()),
+ fake_cursor_(include_cursor ? new FakeMouseMonitor() : nullptr),
+ blender_(fake_screen_, fake_cursor_) {
+ blender_.Start(this);
+ }
+
+ // DesktopCapturer::Callback interface
+ void OnCaptureResult(DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame> frame) override {
+ frame_ = std::move(frame);
+ }
+
+ protected:
+ // Owned by `blender_`.
+ FakeScreenCapturer* fake_screen_;
+ FakeMouseMonitor* fake_cursor_;
+
+ DesktopAndCursorComposer blender_;
+ std::unique_ptr<DesktopFrame> frame_;
+};
+
+class DesktopAndCursorComposerNoCursorMonitorTest
+ : public DesktopAndCursorComposerTest {
+ public:
+ DesktopAndCursorComposerNoCursorMonitorTest()
+ : DesktopAndCursorComposerTest(false) {}
+};
+
+TEST_F(DesktopAndCursorComposerTest, CursorShouldBeIgnoredIfNoFrameCaptured) {
+ struct {
+ int x, y;
+ int hotspot_x, hotspot_y;
+ bool inside;
+ } tests[] = {
+ {0, 0, 0, 0, true}, {50, 50, 0, 0, true}, {100, 50, 0, 0, true},
+ {50, 100, 0, 0, true}, {100, 100, 0, 0, true}, {0, 0, 2, 5, true},
+ {1, 1, 2, 5, true}, {50, 50, 2, 5, true}, {100, 100, 2, 5, true},
+ {0, 0, 5, 2, true}, {50, 50, 5, 2, true}, {100, 100, 5, 2, true},
+ {0, 0, 0, 0, false},
+ };
+
+ for (size_t i = 0; i < arraysize(tests); i++) {
+ SCOPED_TRACE(i);
+
+ DesktopVector hotspot(tests[i].hotspot_x, tests[i].hotspot_y);
+ fake_cursor_->SetHotspot(hotspot);
+
+ MouseCursorMonitor::CursorState state = tests[i].inside
+ ? MouseCursorMonitor::INSIDE
+ : MouseCursorMonitor::OUTSIDE;
+ DesktopVector pos(tests[i].x, tests[i].y);
+ fake_cursor_->SetState(state, pos);
+
+ std::unique_ptr<SharedDesktopFrame> frame(
+ SharedDesktopFrame::Wrap(CreateTestFrame()));
+
+ blender_.CaptureFrame();
+ // If capturer captured nothing, then cursor should be ignored, not matter
+ // its state or position.
+ EXPECT_EQ(frame_, nullptr);
+ }
+}
+
+TEST_F(DesktopAndCursorComposerTest, CursorShouldBeIgnoredIfFrameMayContainIt) {
+ // We can't use a shared frame because we need to detect modifications
+ // compared to a control.
+ std::unique_ptr<DesktopFrame> control_frame(CreateTestFrame());
+ control_frame->set_top_left(DesktopVector(kFrameXCoord, kFrameYCoord));
+
+ struct {
+ int x;
+ int y;
+ bool may_contain_cursor;
+ } tests[] = {
+ {100, 200, true},
+ {100, 200, false},
+ {150, 250, true},
+ {150, 250, false},
+ };
+
+ for (size_t i = 0; i < arraysize(tests); i++) {
+ SCOPED_TRACE(i);
+
+ std::unique_ptr<DesktopFrame> frame(CreateTestFrame());
+ frame->set_top_left(DesktopVector(kFrameXCoord, kFrameYCoord));
+ frame->set_may_contain_cursor(tests[i].may_contain_cursor);
+ fake_screen_->SetNextFrame(std::move(frame));
+
+ const DesktopVector abs_pos(tests[i].x, tests[i].y);
+ fake_cursor_->SetState(MouseCursorMonitor::INSIDE, abs_pos);
+ blender_.CaptureFrame();
+
+ // If the frame may already have contained the cursor, then `CaptureFrame()`
+ // should not have modified it, so it should be the same as the control.
+ EXPECT_TRUE(frame_);
+ const DesktopVector rel_pos(abs_pos.subtract(control_frame->top_left()));
+ if (tests[i].may_contain_cursor) {
+ EXPECT_EQ(
+ *reinterpret_cast<uint32_t*>(frame_->GetFrameDataAtPos(rel_pos)),
+ *reinterpret_cast<uint32_t*>(
+ control_frame->GetFrameDataAtPos(rel_pos)));
+
+ } else {
+ // `CaptureFrame()` should have modified the frame to have the cursor.
+ EXPECT_NE(
+ *reinterpret_cast<uint32_t*>(frame_->GetFrameDataAtPos(rel_pos)),
+ *reinterpret_cast<uint32_t*>(
+ control_frame->GetFrameDataAtPos(rel_pos)));
+ EXPECT_TRUE(frame_->may_contain_cursor());
+ }
+ }
+}
+
+TEST_F(DesktopAndCursorComposerTest,
+ CursorShouldBeIgnoredIfItIsOutOfDesktopFrame) {
+ std::unique_ptr<SharedDesktopFrame> frame(
+ SharedDesktopFrame::Wrap(CreateTestFrame()));
+ frame->set_top_left(DesktopVector(kFrameXCoord, kFrameYCoord));
+ // The frame covers (100, 200) - (200, 300).
+
+ struct {
+ int x;
+ int y;
+ } tests[] = {
+ {0, 0}, {50, 50}, {50, 150}, {100, 150}, {50, 200},
+ {99, 200}, {100, 199}, {200, 300}, {200, 299}, {199, 300},
+ {-1, -1}, {-10000, -10000}, {10000, 10000},
+ };
+ for (size_t i = 0; i < arraysize(tests); i++) {
+ SCOPED_TRACE(i);
+
+ fake_screen_->SetNextFrame(frame->Share());
+ // The CursorState is ignored when using absolute cursor position.
+ fake_cursor_->SetState(MouseCursorMonitor::OUTSIDE,
+ DesktopVector(tests[i].x, tests[i].y));
+ blender_.CaptureFrame();
+ VerifyFrame(*frame_, MouseCursorMonitor::OUTSIDE, DesktopVector(0, 0));
+ }
+}
+
+TEST_F(DesktopAndCursorComposerTest, IsOccludedShouldBeConsidered) {
+ std::unique_ptr<SharedDesktopFrame> frame(
+ SharedDesktopFrame::Wrap(CreateTestFrame()));
+ frame->set_top_left(DesktopVector(kFrameXCoord, kFrameYCoord));
+ // The frame covers (100, 200) - (200, 300).
+
+ struct {
+ int x;
+ int y;
+ } tests[] = {
+ {100, 200}, {101, 200}, {100, 201}, {101, 201}, {150, 250}, {199, 299},
+ };
+ fake_screen_->set_is_occluded(true);
+ for (size_t i = 0; i < arraysize(tests); i++) {
+ SCOPED_TRACE(i);
+
+ fake_screen_->SetNextFrame(frame->Share());
+ // The CursorState is ignored when using absolute cursor position.
+ fake_cursor_->SetState(MouseCursorMonitor::OUTSIDE,
+ DesktopVector(tests[i].x, tests[i].y));
+ blender_.CaptureFrame();
+ VerifyFrame(*frame_, MouseCursorMonitor::OUTSIDE, DesktopVector());
+ }
+}
+
+TEST_F(DesktopAndCursorComposerTest, CursorIncluded) {
+ std::unique_ptr<SharedDesktopFrame> frame(
+ SharedDesktopFrame::Wrap(CreateTestFrame()));
+ frame->set_top_left(DesktopVector(kFrameXCoord, kFrameYCoord));
+ // The frame covers (100, 200) - (200, 300).
+
+ struct {
+ int x;
+ int y;
+ } tests[] = {
+ {100, 200}, {101, 200}, {100, 201}, {101, 201}, {150, 250}, {199, 299},
+ };
+ for (size_t i = 0; i < arraysize(tests); i++) {
+ SCOPED_TRACE(i);
+
+ const DesktopVector abs_pos(tests[i].x, tests[i].y);
+ const DesktopVector rel_pos(abs_pos.subtract(frame->top_left()));
+
+ fake_screen_->SetNextFrame(frame->Share());
+ // The CursorState is ignored when using absolute cursor position.
+ fake_cursor_->SetState(MouseCursorMonitor::OUTSIDE, abs_pos);
+ blender_.CaptureFrame();
+ VerifyFrame(*frame_, MouseCursorMonitor::INSIDE, rel_pos);
+
+ // Verify that the cursor is erased before the frame buffer is returned to
+ // the screen capturer.
+ frame_.reset();
+ VerifyFrame(*frame, MouseCursorMonitor::OUTSIDE, DesktopVector());
+ }
+}
+
+TEST_F(DesktopAndCursorComposerNoCursorMonitorTest,
+ UpdatedRegionIncludesOldAndNewCursorRectsIfMoved) {
+ std::unique_ptr<SharedDesktopFrame> frame(
+ SharedDesktopFrame::Wrap(CreateTestFrame()));
+ DesktopRect first_cursor_rect;
+ {
+ // Block to scope test_cursor, which is invalidated by OnMouseCursor.
+ MouseCursor* test_cursor = CreateTestCursor(DesktopVector(0, 0));
+ first_cursor_rect = DesktopRect::MakeSize(test_cursor->image()->size());
+ blender_.OnMouseCursor(test_cursor);
+ }
+ blender_.OnMouseCursorPosition(DesktopVector(0, 0));
+ fake_screen_->SetNextFrame(frame->Share());
+ blender_.CaptureFrame();
+
+ DesktopVector cursor_move_offset(1, 1);
+ DesktopRect second_cursor_rect = first_cursor_rect;
+ second_cursor_rect.Translate(cursor_move_offset);
+ blender_.OnMouseCursorPosition(cursor_move_offset);
+ fake_screen_->SetNextFrame(frame->Share());
+ blender_.CaptureFrame();
+
+ EXPECT_TRUE(frame->updated_region().is_empty());
+ DesktopRegion expected_region;
+ expected_region.AddRect(first_cursor_rect);
+ expected_region.AddRect(second_cursor_rect);
+ EXPECT_TRUE(frame_->updated_region().Equals(expected_region));
+}
+
+TEST_F(DesktopAndCursorComposerNoCursorMonitorTest,
+ UpdatedRegionIncludesOldAndNewCursorRectsIfShapeChanged) {
+ std::unique_ptr<SharedDesktopFrame> frame(
+ SharedDesktopFrame::Wrap(CreateTestFrame()));
+ DesktopRect first_cursor_rect;
+ {
+ // Block to scope test_cursor, which is invalidated by OnMouseCursor.
+ MouseCursor* test_cursor = CreateTestCursor(DesktopVector(0, 0));
+ first_cursor_rect = DesktopRect::MakeSize(test_cursor->image()->size());
+ blender_.OnMouseCursor(test_cursor);
+ }
+ blender_.OnMouseCursorPosition(DesktopVector(0, 0));
+ fake_screen_->SetNextFrame(frame->Share());
+ blender_.CaptureFrame();
+
+ // Create a second cursor, the same shape as the first. Since the code doesn't
+ // compare the cursor pixels, this is sufficient, and avoids needing two test
+ // cursor bitmaps.
+ DesktopRect second_cursor_rect;
+ {
+ MouseCursor* test_cursor = CreateTestCursor(DesktopVector(0, 0));
+ second_cursor_rect = DesktopRect::MakeSize(test_cursor->image()->size());
+ blender_.OnMouseCursor(test_cursor);
+ }
+ fake_screen_->SetNextFrame(frame->Share());
+ blender_.CaptureFrame();
+
+ EXPECT_TRUE(frame->updated_region().is_empty());
+ DesktopRegion expected_region;
+ expected_region.AddRect(first_cursor_rect);
+ expected_region.AddRect(second_cursor_rect);
+ EXPECT_TRUE(frame_->updated_region().Equals(expected_region));
+}
+
+TEST_F(DesktopAndCursorComposerNoCursorMonitorTest,
+ UpdatedRegionUnchangedIfCursorUnchanged) {
+ std::unique_ptr<SharedDesktopFrame> frame(
+ SharedDesktopFrame::Wrap(CreateTestFrame()));
+ blender_.OnMouseCursor(CreateTestCursor(DesktopVector(0, 0)));
+ blender_.OnMouseCursorPosition(DesktopVector(0, 0));
+ fake_screen_->SetNextFrame(frame->Share());
+ blender_.CaptureFrame();
+ fake_screen_->SetNextFrame(frame->Share());
+ blender_.CaptureFrame();
+
+ EXPECT_TRUE(frame->updated_region().is_empty());
+ EXPECT_TRUE(frame_->updated_region().is_empty());
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_differ_sse2_gn/moz.build b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_differ_sse2_gn/moz.build
new file mode 100644
index 0000000000..1d9b39c90e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_differ_sse2_gn/moz.build
@@ -0,0 +1,142 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_AVX2"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/differ_vector_sse2.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_GNU_SOURCE"] = True
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ CXXFLAGS += [
+ "-msse2"
+ ]
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+Library("desktop_capture_differ_sse2_gn")
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_gn/moz.build b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_gn/moz.build
new file mode 100644
index 0000000000..8631744ab1
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_gn/moz.build
@@ -0,0 +1,439 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "/ipc/chromium/src",
+ "/media/libyuv/",
+ "/media/libyuv/libyuv/include/",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/third_party/pipewire/",
+ "/tools/profiler/public"
+]
+
+SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/fallback_desktop_capturer_wrapper.cc"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/blank_detector_desktop_capturer_wrapper.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/cropped_desktop_frame.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/cropping_window_capturer.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/desktop_and_cursor_composer.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/desktop_capture_metrics_helper.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/desktop_capturer.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_differ_wrapper.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_wrapper.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/desktop_frame_generator.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/desktop_frame_rotation.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/differ_block.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/fake_desktop_capturer.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/full_screen_application_handler.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/mouse_cursor.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/resolution_tracker.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/rgba_color.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/screen_capturer_helper.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/window_finder.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.cc"
+ ]
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "rt"
+ ]
+
+ UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/screen_capturer_linux.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/window_capturer_linux.cc"
+ ]
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["WEBRTC_USE_X11"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+ OS_LIBS += [
+ "X11",
+ "Xcomposite",
+ "Xdamage",
+ "Xext",
+ "Xfixes",
+ "Xrandr",
+ "Xrender",
+ "Xtst"
+ ]
+
+ UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/screen_capturer_linux.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/window_capturer_linux.cc"
+ ]
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+ OS_LIBS += [
+ "crypt32",
+ "d3d11",
+ "dxgi",
+ "iphlpapi",
+ "secur32",
+ "winmm"
+ ]
+
+ SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc"
+ ]
+
+ UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/cropping_window_capturer_win.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/desktop_frame_win.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/screen_capturer_win.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/cursor.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/d3d_device.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/desktop.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/window_capturer_win.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/window_finder_win.cc"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+ DEFINES["WEBRTC_USE_PIPEWIRE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+ SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.cc"
+ ]
+
+if CONFIG["CPU_ARCH"] == "ppc64":
+
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_USE_X11"] = True
+
+ OS_LIBS += [
+ "X11",
+ "Xcomposite",
+ "Xdamage",
+ "Xext",
+ "Xfixes",
+ "Xrandr",
+ "Xrender",
+ "Xtst"
+ ]
+
+ UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.cc"
+ ]
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_USE_PIPEWIRE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+ SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.cc"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_USE_PIPEWIRE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+ SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.cc"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_USE_PIPEWIRE"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+ SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.cc"
+ ]
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_USE_X11"] = True
+
+ OS_LIBS += [
+ "X11",
+ "Xcomposite",
+ "Xdamage",
+ "Xext",
+ "Xfixes",
+ "Xrandr",
+ "Xrender",
+ "Xtst"
+ ]
+
+ UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.cc"
+ ]
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_USE_X11"] = True
+
+ OS_LIBS += [
+ "X11",
+ "Xcomposite",
+ "Xdamage",
+ "Xext",
+ "Xfixes",
+ "Xrandr",
+ "Xrender",
+ "Xtst"
+ ]
+
+ UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.cc"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_USE_X11"] = True
+
+ OS_LIBS += [
+ "X11",
+ "Xcomposite",
+ "Xdamage",
+ "Xext",
+ "Xfixes",
+ "Xrandr",
+ "Xrender",
+ "Xtst"
+ ]
+
+ UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.cc"
+ ]
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_USE_X11"] = True
+
+ OS_LIBS += [
+ "X11",
+ "Xcomposite",
+ "Xdamage",
+ "Xext",
+ "Xfixes",
+ "Xrandr",
+ "Xrender",
+ "Xtst"
+ ]
+
+ UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.cc"
+ ]
+
+Library("desktop_capture_gn")
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_metadata.h b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_metadata.h
new file mode 100644
index 0000000000..faca156e33
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_metadata.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_METADATA_H_
+#define MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_METADATA_H_
+
+#if defined(WEBRTC_USE_GIO)
+#include "modules/desktop_capture/linux/wayland/xdg_session_details.h"
+#endif // defined(WEBRTC_USE_GIO)
+
+namespace webrtc {
+
+// Container for the metadata associated with a desktop capturer.
+struct DesktopCaptureMetadata {
+#if defined(WEBRTC_USE_GIO)
+ // Details about the XDG desktop session handle (used by wayland
+ // implementation in remoting)
+ xdg_portal::SessionDetails session_details;
+#endif // defined(WEBRTC_USE_GIO)
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_METADATA_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_metrics_helper.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_metrics_helper.cc
new file mode 100644
index 0000000000..6b741ef4bb
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_metrics_helper.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_capture_metrics_helper.h"
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+namespace {
+// This enum is logged via UMA so entries should not be reordered or have their
+// values changed. This should also be kept in sync with the values in the
+// DesktopCapturerId namespace.
+enum class SequentialDesktopCapturerId {
+ kUnknown = 0,
+ kWgcCapturerWin = 1,
+ kScreenCapturerWinMagnifier = 2,
+ kWindowCapturerWinGdi = 3,
+ kScreenCapturerWinGdi = 4,
+ kScreenCapturerWinDirectx = 5,
+ kMaxValue = kScreenCapturerWinDirectx
+};
+} // namespace
+
+void RecordCapturerImpl(uint32_t capturer_id) {
+ SequentialDesktopCapturerId sequential_id;
+ switch (capturer_id) {
+ case DesktopCapturerId::kWgcCapturerWin:
+ sequential_id = SequentialDesktopCapturerId::kWgcCapturerWin;
+ break;
+ case DesktopCapturerId::kScreenCapturerWinMagnifier:
+ sequential_id = SequentialDesktopCapturerId::kScreenCapturerWinMagnifier;
+ break;
+ case DesktopCapturerId::kWindowCapturerWinGdi:
+ sequential_id = SequentialDesktopCapturerId::kWindowCapturerWinGdi;
+ break;
+ case DesktopCapturerId::kScreenCapturerWinGdi:
+ sequential_id = SequentialDesktopCapturerId::kScreenCapturerWinGdi;
+ break;
+ case DesktopCapturerId::kScreenCapturerWinDirectx:
+ sequential_id = SequentialDesktopCapturerId::kScreenCapturerWinDirectx;
+ break;
+ case DesktopCapturerId::kUnknown:
+ default:
+ sequential_id = SequentialDesktopCapturerId::kUnknown;
+ }
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.DesktopCapture.Win.DesktopCapturerImpl",
+ static_cast<int>(sequential_id),
+ static_cast<int>(SequentialDesktopCapturerId::kMaxValue));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_metrics_helper.h b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_metrics_helper.h
new file mode 100644
index 0000000000..37542b84bb
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_metrics_helper.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_METRICS_HELPER_H_
+#define MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_METRICS_HELPER_H_
+
+#include <stdint.h>
+
+namespace webrtc {
+
+void RecordCapturerImpl(uint32_t capturer_id);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_METRICS_HELPER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_objc_gn/moz.build b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_objc_gn/moz.build
new file mode 100644
index 0000000000..e884abb54d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_objc_gn/moz.build
@@ -0,0 +1,72 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+CMMFLAGS += [
+ "-fobjc-arc"
+]
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_AVX2"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MAC"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+DEFINES["WEBRTC_POSIX"] = True
+DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+DEFINES["__STDC_CONSTANT_MACROS"] = True
+DEFINES["__STDC_FORMAT_MACROS"] = True
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "/ipc/chromium/src",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/sdk/objc/",
+ "/third_party/libwebrtc/sdk/objc/base/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.mm",
+ "/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.mm",
+ "/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.mm",
+ "/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.mm",
+ "/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.mm",
+ "/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm",
+ "/third_party/libwebrtc/modules/desktop_capture/screen_capturer_darwin.mm",
+ "/third_party/libwebrtc/modules/desktop_capture/window_capturer_mac.mm",
+ "/third_party/libwebrtc/modules/desktop_capture/window_finder_mac.mm"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+Library("desktop_capture_objc_gn")
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.cc
new file mode 100644
index 0000000000..767dbfefa5
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+
+#include "api/make_ref_counted.h"
+
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+#include "modules/desktop_capture/mac/full_screen_mac_application_handler.h"
+#elif defined(WEBRTC_WIN)
+#include "modules/desktop_capture/win/full_screen_win_application_handler.h"
+#endif
+#if defined(WEBRTC_USE_PIPEWIRE) && !defined(WEBRTC_MOZILLA_BUILD)
+#include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h"
+#endif
+
+namespace webrtc {
+
+DesktopCaptureOptions::DesktopCaptureOptions() {}
+DesktopCaptureOptions::DesktopCaptureOptions(
+ const DesktopCaptureOptions& options) = default;
+DesktopCaptureOptions::DesktopCaptureOptions(DesktopCaptureOptions&& options) =
+ default;
+DesktopCaptureOptions::~DesktopCaptureOptions() {}
+
+DesktopCaptureOptions& DesktopCaptureOptions::operator=(
+ const DesktopCaptureOptions& options) = default;
+DesktopCaptureOptions& DesktopCaptureOptions::operator=(
+ DesktopCaptureOptions&& options) = default;
+
+// static
+DesktopCaptureOptions DesktopCaptureOptions::CreateDefault() {
+ DesktopCaptureOptions result;
+#if defined(WEBRTC_USE_X11)
+ result.set_x_display(SharedXDisplay::CreateDefault());
+#endif
+#if defined(WEBRTC_USE_PIPEWIRE) && !defined(WEBRTC_MOZILLA_BUILD)
+ result.set_screencast_stream(SharedScreenCastStream::CreateDefault());
+#endif
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+ result.set_configuration_monitor(
+ rtc::make_ref_counted<DesktopConfigurationMonitor>());
+ result.set_full_screen_window_detector(
+ rtc::make_ref_counted<FullScreenWindowDetector>(
+ CreateFullScreenMacApplicationHandler));
+#elif defined(WEBRTC_WIN)
+ result.set_full_screen_window_detector(
+ rtc::make_ref_counted<FullScreenWindowDetector>(
+ CreateFullScreenWinApplicationHandler));
+#endif
+ return result;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.h b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.h
new file mode 100644
index 0000000000..c11efdb615
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_options.h
@@ -0,0 +1,244 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_OPTIONS_H_
+#define MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_OPTIONS_H_
+
+#include "api/scoped_refptr.h"
+#include "rtc_base/system/rtc_export.h"
+
+#if defined(WEBRTC_USE_X11)
+#include "modules/desktop_capture/linux/x11/shared_x_display.h"
+#endif
+
+#if defined(WEBRTC_USE_PIPEWIRE) && !defined(WEBRTC_MOZILLA_BUILD)
+#include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h"
+#endif
+
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+#include "modules/desktop_capture/mac/desktop_configuration_monitor.h"
+#endif
+
+#include "modules/desktop_capture/full_screen_window_detector.h"
+
+namespace webrtc {
+
+// An object that stores initialization parameters for screen and window
+// capturers.
+class RTC_EXPORT DesktopCaptureOptions {
+ public:
+ // Returns instance of DesktopCaptureOptions with default parameters. On Linux
+ // also initializes X window connection. x_display() will be set to null if
+ // X11 connection failed (e.g. DISPLAY isn't set).
+ static DesktopCaptureOptions CreateDefault();
+
+ DesktopCaptureOptions();
+ DesktopCaptureOptions(const DesktopCaptureOptions& options);
+ DesktopCaptureOptions(DesktopCaptureOptions&& options);
+ ~DesktopCaptureOptions();
+
+ DesktopCaptureOptions& operator=(const DesktopCaptureOptions& options);
+ DesktopCaptureOptions& operator=(DesktopCaptureOptions&& options);
+
+#if defined(WEBRTC_USE_X11)
+ const rtc::scoped_refptr<SharedXDisplay>& x_display() const {
+ return x_display_;
+ }
+ void set_x_display(rtc::scoped_refptr<SharedXDisplay> x_display) {
+ x_display_ = x_display;
+ }
+#endif
+
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+ // TODO(zijiehe): Remove both DesktopConfigurationMonitor and
+ // FullScreenChromeWindowDetector out of DesktopCaptureOptions. It's not
+ // reasonable for external consumers to set these two parameters.
+ const rtc::scoped_refptr<DesktopConfigurationMonitor>& configuration_monitor()
+ const {
+ return configuration_monitor_;
+ }
+ // If nullptr is set, ScreenCapturer won't work and WindowCapturer may return
+ // inaccurate result from IsOccluded() function.
+ void set_configuration_monitor(
+ rtc::scoped_refptr<DesktopConfigurationMonitor> m) {
+ configuration_monitor_ = m;
+ }
+
+ bool allow_iosurface() const { return allow_iosurface_; }
+ void set_allow_iosurface(bool allow) { allow_iosurface_ = allow; }
+#endif
+
+ const rtc::scoped_refptr<FullScreenWindowDetector>&
+ full_screen_window_detector() const {
+ return full_screen_window_detector_;
+ }
+ void set_full_screen_window_detector(
+ rtc::scoped_refptr<FullScreenWindowDetector> detector) {
+ full_screen_window_detector_ = detector;
+ }
+
+ // Flag indicating that the capturer should use screen change notifications.
+ // Enables/disables use of XDAMAGE in the X11 capturer.
+ bool use_update_notifications() const { return use_update_notifications_; }
+ void set_use_update_notifications(bool use_update_notifications) {
+ use_update_notifications_ = use_update_notifications;
+ }
+
+ // Flag indicating if desktop effects (e.g. Aero) should be disabled when the
+ // capturer is active. Currently used only on Windows.
+ bool disable_effects() const { return disable_effects_; }
+ void set_disable_effects(bool disable_effects) {
+ disable_effects_ = disable_effects;
+ }
+
+ // Flag that should be set if the consumer uses updated_region() and the
+ // capturer should try to provide correct updated_region() for the frames it
+ // generates (e.g. by comparing each frame with the previous one).
+ bool detect_updated_region() const { return detect_updated_region_; }
+ void set_detect_updated_region(bool detect_updated_region) {
+ detect_updated_region_ = detect_updated_region;
+ }
+
+#if defined(WEBRTC_WIN)
+ // Enumerating windows owned by the current process on Windows has some
+ // complications due to |GetWindowText*()| APIs potentially causing a
+ // deadlock (see the comments in the `GetWindowListHandler()` function in
+ // window_capture_utils.cc for more details on the deadlock).
+ // To avoid this issue, consumers can either ensure that the thread that runs
+ // their message loop never waits on `GetSourceList()`, or they can set this
+ // flag to false which will prevent windows running in the current process
+ // from being enumerated and included in the results. Consumers can still
+ // provide the WindowId for their own windows to `SelectSource()` and capture
+ // them.
+ bool enumerate_current_process_windows() const {
+ return enumerate_current_process_windows_;
+ }
+ void set_enumerate_current_process_windows(
+ bool enumerate_current_process_windows) {
+ enumerate_current_process_windows_ = enumerate_current_process_windows;
+ }
+
+ bool allow_use_magnification_api() const {
+ return allow_use_magnification_api_;
+ }
+ void set_allow_use_magnification_api(bool allow) {
+ allow_use_magnification_api_ = allow;
+ }
+ // Allowing directx based capturer or not, this capturer works on windows 7
+ // with platform update / windows 8 or upper.
+ bool allow_directx_capturer() const { return allow_directx_capturer_; }
+ void set_allow_directx_capturer(bool enabled) {
+ allow_directx_capturer_ = enabled;
+ }
+
+ // Flag that may be set to allow use of the cropping window capturer (which
+ // captures the screen & crops that to the window region in some cases). An
+ // advantage of using this is significantly higher capture frame rates than
+ // capturing the window directly. A disadvantage of using this is the
+ // possibility of capturing unrelated content (e.g. overlapping windows that
+ // aren't detected properly, or neighboring regions when moving/resizing the
+ // captured window). Note: this flag influences the behavior of calls to
+ // DesktopCapturer::CreateWindowCapturer; calls to
+ // CroppingWindowCapturer::CreateCapturer ignore the flag (treat it as true).
+ bool allow_cropping_window_capturer() const {
+ return allow_cropping_window_capturer_;
+ }
+ void set_allow_cropping_window_capturer(bool allow) {
+ allow_cropping_window_capturer_ = allow;
+ }
+
+#if defined(RTC_ENABLE_WIN_WGC)
+ // This flag enables the WGC capturer for both window and screen capture.
+ // This capturer should offer similar or better performance than the cropping
+ // capturer without the disadvantages listed above. However, the WGC capturer
+ // is only available on Windows 10 version 1809 (Redstone 5) and up. This flag
+ // will have no affect on older versions.
+ // If set, and running a supported version of Win10, this flag will take
+ // precedence over the cropping, directx, and magnification flags.
+ bool allow_wgc_capturer() const { return allow_wgc_capturer_; }
+ void set_allow_wgc_capturer(bool allow) { allow_wgc_capturer_ = allow; }
+
+ // This flag enables the WGC capturer for fallback capturer.
+ // The flag is useful when the first capturer (eg. WindowCapturerWinGdi) is
+ // unreliable in certain devices where WGC is supported, but not used by
+ // default.
+ bool allow_wgc_capturer_fallback() const {
+ return allow_wgc_capturer_fallback_;
+ }
+ void set_allow_wgc_capturer_fallback(bool allow) {
+ allow_wgc_capturer_fallback_ = allow;
+ }
+#endif // defined(RTC_ENABLE_WIN_WGC)
+#endif // defined(WEBRTC_WIN)
+
+#if defined(WEBRTC_USE_PIPEWIRE)
+ bool allow_pipewire() const { return allow_pipewire_; }
+ void set_allow_pipewire(bool allow) { allow_pipewire_ = allow; }
+
+#if !defined(WEBRTC_MOZILLA_BUILD)
+ const rtc::scoped_refptr<SharedScreenCastStream>& screencast_stream() const {
+ return screencast_stream_;
+ }
+ void set_screencast_stream(
+ rtc::scoped_refptr<SharedScreenCastStream> stream) {
+ screencast_stream_ = stream;
+ }
+
+ void set_width(uint32_t width) { width_ = width; }
+ uint32_t get_width() const { return width_; }
+
+ void set_height(uint32_t height) { height_ = height; }
+ uint32_t get_height() const { return height_; }
+#endif
+#endif
+
+ private:
+#if defined(WEBRTC_USE_X11)
+ rtc::scoped_refptr<SharedXDisplay> x_display_;
+#endif
+#if defined(WEBRTC_USE_PIPEWIRE) && !defined(WEBRTC_MOZILLA_BUILD)
+ // An instance of shared PipeWire ScreenCast stream we share between
+ // BaseCapturerPipeWire and MouseCursorMonitorPipeWire as cursor information
+ // is sent together with screen content.
+ rtc::scoped_refptr<SharedScreenCastStream> screencast_stream_;
+#endif
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+ rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
+ bool allow_iosurface_ = false;
+#endif
+
+ rtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector_;
+
+#if defined(WEBRTC_WIN)
+ bool enumerate_current_process_windows_ = true;
+ bool allow_use_magnification_api_ = false;
+ bool allow_directx_capturer_ = false;
+ bool allow_cropping_window_capturer_ = false;
+#if defined(RTC_ENABLE_WIN_WGC)
+ bool allow_wgc_capturer_ = false;
+ bool allow_wgc_capturer_fallback_ = false;
+#endif
+#endif
+#if defined(WEBRTC_USE_X11)
+ bool use_update_notifications_ = false;
+#else
+ bool use_update_notifications_ = true;
+#endif
+ bool disable_effects_ = true;
+ bool detect_updated_region_ = false;
+#if defined(WEBRTC_USE_PIPEWIRE)
+ bool allow_pipewire_ = false;
+ uint32_t width_ = 0;
+ uint32_t height_ = 0;
+#endif
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_OPTIONS_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capture_types.h b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_types.h
new file mode 100644
index 0000000000..94be2fb68b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capture_types.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_TYPES_H_
+#define MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_TYPES_H_
+
+#ifndef XP_WIN
+#include <sys/types.h> // pid_t
+#endif
+#include <stdint.h>
+
+#ifdef XP_WIN // Moving this into the global namespace
+typedef int pid_t; // matching what used to be in
+#endif // video_capture_defines.h
+
+namespace webrtc {
+
+enum class CaptureType { kWindow, kScreen };
+
+// Type used to identify windows on the desktop. Values are platform-specific:
+// - On Windows: HWND cast to intptr_t.
+// - On Linux (with X11): X11 Window (unsigned long) type cast to intptr_t.
+// - On OSX: integer window number.
+typedef intptr_t WindowId;
+
+const WindowId kNullWindowId = 0;
+
+// Type used to identify screens on the desktop. Values are platform-specific:
+// - On Windows: integer display device index.
+// - On OSX: CGDirectDisplayID cast to intptr_t.
+// - On Linux (with X11): TBD.
+// - On ChromeOS: display::Display::id() is an int64_t.
+// On Windows, ScreenId is implementation dependent: sending a ScreenId from one
+// implementation to another usually won't work correctly.
+#if defined(CHROMEOS)
+ typedef int64_t ScreenId;
+#else
+ typedef intptr_t ScreenId;
+#endif
+
+// The screen id corresponds to all screen combined together.
+const ScreenId kFullDesktopScreenId = -1;
+
+const ScreenId kInvalidScreenId = -2;
+
+typedef intptr_t ProcessId;
+const ProcessId DesktopProcessId = 0;
+
+// Integers to attach to each DesktopFrame to differentiate the generator of
+// the frame. The entries in this namespace should remain in sync with the
+// SequentialDesktopCapturerId enum, which is logged via UMA.
+// `kScreenCapturerWinGdi` and `kScreenCapturerWinDirectx` values are preserved
+// to maintain compatibility
+namespace DesktopCapturerId {
+constexpr uint32_t CreateFourCC(char a, char b, char c, char d) {
+ return ((static_cast<uint32_t>(a)) | (static_cast<uint32_t>(b) << 8) |
+ (static_cast<uint32_t>(c) << 16) | (static_cast<uint32_t>(d) << 24));
+}
+
+constexpr uint32_t kUnknown = 0;
+constexpr uint32_t kWgcCapturerWin = 1;
+constexpr uint32_t kScreenCapturerWinMagnifier = 2;
+constexpr uint32_t kWindowCapturerWinGdi = 3;
+constexpr uint32_t kScreenCapturerWinGdi = CreateFourCC('G', 'D', 'I', ' ');
+constexpr uint32_t kScreenCapturerWinDirectx = CreateFourCC('D', 'X', 'G', 'I');
+} // namespace DesktopCapturerId
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_TYPES_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capturer.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_capturer.cc
new file mode 100644
index 0000000000..9022652ec2
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capturer.cc
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_capturer.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <cstring>
+#include <utility>
+
+#include "modules/desktop_capture/cropping_window_capturer.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer_differ_wrapper.h"
+
+#if defined(RTC_ENABLE_WIN_WGC)
+#include "modules/desktop_capture/win/wgc_capturer_win.h"
+#include "rtc_base/win/windows_version.h"
+#endif // defined(RTC_ENABLE_WIN_WGC)
+
+#if defined(WEBRTC_USE_PIPEWIRE) || defined(WEBRTC_USE_X11)
+#include <gtk/gtk.h>
+#include <gtk/gtkx.h>
+#endif
+
+namespace webrtc {
+
+DesktopCapturer::~DesktopCapturer() = default;
+
+void DesktopCapturer::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {}
+
+void DesktopCapturer::SetExcludedWindow(WindowId window) {}
+
+bool DesktopCapturer::GetSourceList(SourceList* sources) {
+ return true;
+}
+
+bool DesktopCapturer::SelectSource(SourceId id) {
+ return false;
+}
+
+bool DesktopCapturer::FocusOnSelectedSource() {
+ return false;
+}
+
+bool DesktopCapturer::IsOccluded(const DesktopVector& pos) {
+ return false;
+}
+
+// static
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateWindowCapturer(
+ const DesktopCaptureOptions& options) {
+#if defined(RTC_ENABLE_WIN_WGC)
+ if (options.allow_wgc_capturer() && IsWgcSupported(CaptureType::kWindow)) {
+ return WgcCapturerWin::CreateRawWindowCapturer(options);
+ }
+#endif // defined(RTC_ENABLE_WIN_WGC)
+
+#if defined(WEBRTC_WIN)
+ if (options.allow_cropping_window_capturer()) {
+ return CroppingWindowCapturer::CreateCapturer(options);
+ }
+#endif // defined(WEBRTC_WIN)
+
+ std::unique_ptr<DesktopCapturer> capturer = CreateRawWindowCapturer(options);
+ if (capturer && options.detect_updated_region()) {
+ capturer.reset(new DesktopCapturerDifferWrapper(std::move(capturer)));
+ }
+
+ return capturer;
+}
+
+// static
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateScreenCapturer(
+ const DesktopCaptureOptions& options) {
+#if defined(RTC_ENABLE_WIN_WGC)
+ if (options.allow_wgc_capturer() && IsWgcSupported(CaptureType::kScreen)) {
+ return WgcCapturerWin::CreateRawScreenCapturer(options);
+ }
+#endif // defined(RTC_ENABLE_WIN_WGC)
+
+ std::unique_ptr<DesktopCapturer> capturer = CreateRawScreenCapturer(options);
+ if (capturer && options.detect_updated_region()) {
+ capturer.reset(new DesktopCapturerDifferWrapper(std::move(capturer)));
+ }
+
+ return capturer;
+}
+
+// static
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateTabCapturer(
+ const DesktopCaptureOptions& options) {
+ std::unique_ptr<DesktopCapturer> capturer = CreateRawTabCapturer(options);
+ if (capturer && options.detect_updated_region()) {
+ capturer.reset(new DesktopCapturerDifferWrapper(std::move(capturer)));
+ }
+
+ return capturer;
+}
+
+#if defined(WEBRTC_USE_PIPEWIRE) || defined(WEBRTC_USE_X11)
+bool DesktopCapturer::IsRunningUnderWayland() {
+ const char* xdg_session_type = getenv("XDG_SESSION_TYPE");
+ if (!xdg_session_type || strncmp(xdg_session_type, "wayland", 7) != 0)
+ return false;
+
+ if (!(getenv("WAYLAND_DISPLAY")))
+ return false;
+
+ return true;
+}
+#endif // defined(WEBRTC_USE_PIPEWIRE) || defined(WEBRTC_USE_X11)
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capturer.h b/third_party/libwebrtc/modules/desktop_capture/desktop_capturer.h
new file mode 100644
index 0000000000..6e9b95e214
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capturer.h
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURER_H_
+#define MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#if defined(WEBRTC_USE_GIO)
+#include "modules/desktop_capture/desktop_capture_metadata.h"
+#endif // defined(WEBRTC_USE_GIO)
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/shared_memory.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+class DesktopCaptureOptions;
+class DesktopFrame;
+
+// Abstract interface for screen and window capturers.
+class RTC_EXPORT DesktopCapturer {
+ public:
+ enum class Result {
+ // The frame was captured successfully.
+ SUCCESS,
+
+ // There was a temporary error. The caller should continue calling
+ // CaptureFrame(), in the expectation that it will eventually recover.
+ ERROR_TEMPORARY,
+
+ // Capture has failed and will keep failing if the caller tries calling
+ // CaptureFrame() again.
+ ERROR_PERMANENT,
+
+ MAX_VALUE = ERROR_PERMANENT
+ };
+
+ // Interface that must be implemented by the DesktopCapturer consumers.
+ class Callback {
+ public:
+ // Called after a frame has been captured. `frame` is not nullptr if and
+ // only if `result` is SUCCESS.
+ virtual void OnCaptureResult(Result result,
+ std::unique_ptr<DesktopFrame> frame) = 0;
+
+ protected:
+ virtual ~Callback() {}
+ };
+
+#if defined(CHROMEOS)
+ typedef int64_t SourceId;
+#else
+ typedef intptr_t SourceId;
+#endif
+
+ static_assert(std::is_same<SourceId, ScreenId>::value,
+ "SourceId should be a same type as ScreenId.");
+
+ struct Source {
+ // The unique id to represent a Source of current DesktopCapturer.
+ SourceId id;
+ pid_t pid;
+
+ // Title of the window or screen in UTF-8 encoding, maybe empty. This field
+ // should not be used to identify a source.
+ std::string title;
+ };
+
+ typedef std::vector<Source> SourceList;
+
+ virtual ~DesktopCapturer();
+
+ // Called at the beginning of a capturing session. `callback` must remain
+ // valid until capturer is destroyed.
+ virtual void Start(Callback* callback) = 0;
+
+ // Sets SharedMemoryFactory that will be used to create buffers for the
+ // captured frames. The factory can be invoked on a thread other than the one
+ // where CaptureFrame() is called. It will be destroyed on the same thread.
+ // Shared memory is currently supported only by some DesktopCapturer
+ // implementations.
+ virtual void SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory);
+
+ // Captures next frame, and involve callback provided by Start() function.
+ // Pending capture requests are canceled when DesktopCapturer is deleted.
+ virtual void CaptureFrame() = 0;
+
+ // Sets the window to be excluded from the captured image in the future
+ // Capture calls. Used to exclude the screenshare notification window for
+ // screen capturing.
+ virtual void SetExcludedWindow(WindowId window);
+
+ // TODO(zijiehe): Following functions should be pure virtual. The default
+ // implementations are for backward compatibility only. Remove default
+ // implementations once all DesktopCapturer implementations in Chromium have
+ // implemented these functions.
+
+ // Gets a list of sources current capturer supports. Returns false in case of
+ // a failure.
+ // For DesktopCapturer implementations to capture screens, this function
+ // should return monitors.
+ // For DesktopCapturer implementations to capture windows, this function
+ // should only return root windows owned by applications.
+ virtual bool GetSourceList(SourceList* sources);
+
+ // Selects a source to be captured. Returns false in case of a failure (e.g.
+ // if there is no source with the specified type and id.)
+ virtual bool SelectSource(SourceId id);
+
+ // Brings the selected source to the front and sets the input focus on it.
+ // Returns false in case of a failure or no source has been selected or the
+ // implementation does not support this functionality.
+ virtual bool FocusOnSelectedSource();
+
+ // Returns true if the `pos` on the selected source is covered by other
+ // elements on the display, and is not visible to the users.
+ // `pos` is in full desktop coordinates, i.e. the top-left monitor always
+ // starts from (0, 0).
+ // The return value if `pos` is out of the scope of the source is undefined.
+ virtual bool IsOccluded(const DesktopVector& pos);
+
+ // Creates a DesktopCapturer instance which targets to capture windows.
+ static std::unique_ptr<DesktopCapturer> CreateWindowCapturer(
+ const DesktopCaptureOptions& options);
+
+ // Creates a DesktopCapturer instance which targets to capture screens.
+ static std::unique_ptr<DesktopCapturer> CreateScreenCapturer(
+ const DesktopCaptureOptions& options);
+
+ // Creates a DesktopCapturer instance which targets to capture tab.
+ static std::unique_ptr<DesktopCapturer> CreateTabCapturer(
+ const DesktopCaptureOptions& options);
+
+#if defined(WEBRTC_USE_PIPEWIRE) || defined(WEBRTC_USE_X11)
+ static bool IsRunningUnderWayland();
+
+ virtual void UpdateResolution(uint32_t width, uint32_t height) {}
+#endif // defined(WEBRTC_USE_PIPEWIRE) || defined(WEBRTC_USE_X11)
+
+#if defined(WEBRTC_USE_GIO)
+ // Populates implementation specific metadata into the passed in pointer.
+ // Classes can choose to override it or use the default no-op implementation.
+ virtual DesktopCaptureMetadata GetMetadata() { return {}; }
+#endif // defined(WEBRTC_USE_GIO)
+
+ protected:
+ // CroppingWindowCapturer needs to create raw capturers without wrappers, so
+ // the following two functions are protected.
+
+ // Creates a platform specific DesktopCapturer instance which targets to
+ // capture windows.
+ static std::unique_ptr<DesktopCapturer> CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options);
+
+ // Creates a platform specific DesktopCapturer instance which targets to
+ // capture screens.
+ static std::unique_ptr<DesktopCapturer> CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options);
+
+ // Creates a platform specific DesktopCapturer instance which targets to
+ // capture apps.
+ static std::unique_ptr<DesktopCapturer> CreateRawAppCapturer(
+ const DesktopCaptureOptions& options);
+
+ // Creates a DesktopCapturer instance which targets to capture tabs
+ static std::unique_ptr<DesktopCapturer> CreateRawTabCapturer(
+ const DesktopCaptureOptions& options);
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_differ_wrapper.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_differ_wrapper.cc
new file mode 100644
index 0000000000..77543e4060
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_differ_wrapper.cc
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_capturer_differ_wrapper.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include <utility>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/differ_block.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/time_utils.h"
+
+namespace webrtc {
+
+namespace {
+
+// Returns true if (0, 0) - (`width`, `height`) vector in `old_buffer` and
+// `new_buffer` are equal. `width` should be less than 32
+// (defined by kBlockSize), otherwise BlockDifference() should be used.
+bool PartialBlockDifference(const uint8_t* old_buffer,
+ const uint8_t* new_buffer,
+ int width,
+ int height,
+ int stride) {
+ RTC_DCHECK_LT(width, kBlockSize);
+ const int width_bytes = width * DesktopFrame::kBytesPerPixel;
+ for (int i = 0; i < height; i++) {
+ if (memcmp(old_buffer, new_buffer, width_bytes) != 0) {
+ return true;
+ }
+ old_buffer += stride;
+ new_buffer += stride;
+ }
+ return false;
+}
+
+// Compares columns in the range of [`left`, `right`), in a row in the
+// range of [`top`, `top` + `height`), starts from `old_buffer` and
+// `new_buffer`, and outputs updated regions into `output`. `stride` is the
+// DesktopFrame::stride().
+void CompareRow(const uint8_t* old_buffer,
+ const uint8_t* new_buffer,
+ const int left,
+ const int right,
+ const int top,
+ const int bottom,
+ const int stride,
+ DesktopRegion* const output) {
+ const int block_x_offset = kBlockSize * DesktopFrame::kBytesPerPixel;
+ const int width = right - left;
+ const int height = bottom - top;
+ const int block_count = (width - 1) / kBlockSize;
+ const int last_block_width = width - block_count * kBlockSize;
+ RTC_DCHECK_GT(last_block_width, 0);
+ RTC_DCHECK_LE(last_block_width, kBlockSize);
+
+ // The first block-column in a continuous dirty area in current block-row.
+ int first_dirty_x_block = -1;
+
+ // We always need to add dirty area into `output` in the last block, so handle
+ // it separatedly.
+ for (int x = 0; x < block_count; x++) {
+ if (BlockDifference(old_buffer, new_buffer, height, stride)) {
+ if (first_dirty_x_block == -1) {
+ // This is the first dirty block in a continuous dirty area.
+ first_dirty_x_block = x;
+ }
+ } else if (first_dirty_x_block != -1) {
+ // The block on the left is the last dirty block in a continuous
+ // dirty area.
+ output->AddRect(
+ DesktopRect::MakeLTRB(first_dirty_x_block * kBlockSize + left, top,
+ x * kBlockSize + left, bottom));
+ first_dirty_x_block = -1;
+ }
+ old_buffer += block_x_offset;
+ new_buffer += block_x_offset;
+ }
+
+ bool last_block_diff;
+ if (last_block_width < kBlockSize) {
+ // The last one is a partial vector.
+ last_block_diff = PartialBlockDifference(old_buffer, new_buffer,
+ last_block_width, height, stride);
+ } else {
+ last_block_diff = BlockDifference(old_buffer, new_buffer, height, stride);
+ }
+ if (last_block_diff) {
+ if (first_dirty_x_block == -1) {
+ first_dirty_x_block = block_count;
+ }
+ output->AddRect(DesktopRect::MakeLTRB(
+ first_dirty_x_block * kBlockSize + left, top, right, bottom));
+ } else if (first_dirty_x_block != -1) {
+ output->AddRect(
+ DesktopRect::MakeLTRB(first_dirty_x_block * kBlockSize + left, top,
+ block_count * kBlockSize + left, bottom));
+ }
+}
+
+// Compares `rect` area in `old_frame` and `new_frame`, and outputs dirty
+// regions into `output`.
+void CompareFrames(const DesktopFrame& old_frame,
+ const DesktopFrame& new_frame,
+ DesktopRect rect,
+ DesktopRegion* const output) {
+ RTC_DCHECK(old_frame.size().equals(new_frame.size()));
+ RTC_DCHECK_EQ(old_frame.stride(), new_frame.stride());
+ rect.IntersectWith(DesktopRect::MakeSize(old_frame.size()));
+
+ const int y_block_count = (rect.height() - 1) / kBlockSize;
+ const int last_y_block_height = rect.height() - y_block_count * kBlockSize;
+ // Offset from the start of one block-row to the next.
+ const int block_y_stride = old_frame.stride() * kBlockSize;
+ const uint8_t* prev_block_row_start =
+ old_frame.GetFrameDataAtPos(rect.top_left());
+ const uint8_t* curr_block_row_start =
+ new_frame.GetFrameDataAtPos(rect.top_left());
+
+ int top = rect.top();
+ // The last row may have a different height, so we handle it separately.
+ for (int y = 0; y < y_block_count; y++) {
+ CompareRow(prev_block_row_start, curr_block_row_start, rect.left(),
+ rect.right(), top, top + kBlockSize, old_frame.stride(), output);
+ top += kBlockSize;
+ prev_block_row_start += block_y_stride;
+ curr_block_row_start += block_y_stride;
+ }
+ CompareRow(prev_block_row_start, curr_block_row_start, rect.left(),
+ rect.right(), top, top + last_y_block_height, old_frame.stride(),
+ output);
+}
+
+} // namespace
+
+DesktopCapturerDifferWrapper::DesktopCapturerDifferWrapper(
+ std::unique_ptr<DesktopCapturer> base_capturer)
+ : base_capturer_(std::move(base_capturer)) {
+ RTC_DCHECK(base_capturer_);
+}
+
+DesktopCapturerDifferWrapper::~DesktopCapturerDifferWrapper() {}
+
+void DesktopCapturerDifferWrapper::Start(DesktopCapturer::Callback* callback) {
+ callback_ = callback;
+ base_capturer_->Start(this);
+}
+
+void DesktopCapturerDifferWrapper::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
+ base_capturer_->SetSharedMemoryFactory(std::move(shared_memory_factory));
+}
+
+void DesktopCapturerDifferWrapper::CaptureFrame() {
+ base_capturer_->CaptureFrame();
+}
+
+void DesktopCapturerDifferWrapper::SetExcludedWindow(WindowId window) {
+ base_capturer_->SetExcludedWindow(window);
+}
+
+bool DesktopCapturerDifferWrapper::GetSourceList(SourceList* sources) {
+ return base_capturer_->GetSourceList(sources);
+}
+
+bool DesktopCapturerDifferWrapper::SelectSource(SourceId id) {
+ return base_capturer_->SelectSource(id);
+}
+
+bool DesktopCapturerDifferWrapper::FocusOnSelectedSource() {
+ return base_capturer_->FocusOnSelectedSource();
+}
+
+bool DesktopCapturerDifferWrapper::IsOccluded(const DesktopVector& pos) {
+ return base_capturer_->IsOccluded(pos);
+}
+
+#if defined(WEBRTC_USE_GIO)
+DesktopCaptureMetadata DesktopCapturerDifferWrapper::GetMetadata() {
+ return base_capturer_->GetMetadata();
+}
+#endif // defined(WEBRTC_USE_GIO)
+
+void DesktopCapturerDifferWrapper::OnCaptureResult(
+ Result result,
+ std::unique_ptr<DesktopFrame> input_frame) {
+ int64_t start_time_nanos = rtc::TimeNanos();
+ if (!input_frame) {
+ callback_->OnCaptureResult(result, nullptr);
+ return;
+ }
+ RTC_DCHECK(result == Result::SUCCESS);
+
+ std::unique_ptr<SharedDesktopFrame> frame =
+ SharedDesktopFrame::Wrap(std::move(input_frame));
+ if (last_frame_ && (last_frame_->size().width() != frame->size().width() ||
+ last_frame_->size().height() != frame->size().height() ||
+ last_frame_->stride() != frame->stride())) {
+ last_frame_.reset();
+ }
+
+ if (last_frame_) {
+ DesktopRegion hints;
+ hints.Swap(frame->mutable_updated_region());
+ for (DesktopRegion::Iterator it(hints); !it.IsAtEnd(); it.Advance()) {
+ CompareFrames(*last_frame_, *frame, it.rect(),
+ frame->mutable_updated_region());
+ }
+ } else {
+ frame->mutable_updated_region()->SetRect(
+ DesktopRect::MakeSize(frame->size()));
+ }
+ last_frame_ = frame->Share();
+
+ frame->set_capture_time_ms(frame->capture_time_ms() +
+ (rtc::TimeNanos() - start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec);
+ callback_->OnCaptureResult(result, std::move(frame));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_differ_wrapper.h b/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_differ_wrapper.h
new file mode 100644
index 0000000000..6ebb5d7bc3
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_differ_wrapper.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURER_DIFFER_WRAPPER_H_
+#define MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURER_DIFFER_WRAPPER_H_
+
+#include <memory>
+#if defined(WEBRTC_USE_GIO)
+#include "modules/desktop_capture/desktop_capture_metadata.h"
+#endif // defined(WEBRTC_USE_GIO)
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "modules/desktop_capture/shared_memory.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// DesktopCapturer wrapper that calculates updated_region() by comparing frames
+// content. This class always expects the underlying DesktopCapturer
+// implementation returns a superset of updated regions in DestkopFrame. If a
+// DesktopCapturer implementation does not know the updated region, it should
+// set updated_region() to full frame.
+//
+// This class marks entire frame as updated if the frame size or frame stride
+// has been changed.
+class RTC_EXPORT DesktopCapturerDifferWrapper
+ : public DesktopCapturer,
+ public DesktopCapturer::Callback {
+ public:
+ // Creates a DesktopCapturerDifferWrapper with a DesktopCapturer
+ // implementation, and takes its ownership.
+ explicit DesktopCapturerDifferWrapper(
+ std::unique_ptr<DesktopCapturer> base_capturer);
+
+ ~DesktopCapturerDifferWrapper() override;
+
+ // DesktopCapturer interface.
+ void Start(DesktopCapturer::Callback* callback) override;
+ void SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) override;
+ void CaptureFrame() override;
+ void SetExcludedWindow(WindowId window) override;
+ bool GetSourceList(SourceList* screens) override;
+ bool SelectSource(SourceId id) override;
+ bool FocusOnSelectedSource() override;
+ bool IsOccluded(const DesktopVector& pos) override;
+#if defined(WEBRTC_USE_GIO)
+ DesktopCaptureMetadata GetMetadata() override;
+#endif // defined(WEBRTC_USE_GIO)
+ private:
+ // DesktopCapturer::Callback interface.
+ void OnCaptureResult(Result result,
+ std::unique_ptr<DesktopFrame> frame) override;
+
+ const std::unique_ptr<DesktopCapturer> base_capturer_;
+ DesktopCapturer::Callback* callback_;
+ std::unique_ptr<SharedDesktopFrame> last_frame_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURER_DIFFER_WRAPPER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_differ_wrapper_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_differ_wrapper_unittest.cc
new file mode 100644
index 0000000000..9ccef3cc10
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_differ_wrapper_unittest.cc
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_capturer_differ_wrapper.h"
+
+#include <initializer_list>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/differ_block.h"
+#include "modules/desktop_capture/fake_desktop_capturer.h"
+#include "modules/desktop_capture/mock_desktop_capturer_callback.h"
+#include "rtc_base/random.h"
+#include "rtc_base/time_utils.h"
+#include "system_wrappers/include/cpu_features_wrapper.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+// Compares and asserts `frame`.updated_region() equals to `rects`. This
+// function does not care about the order of the `rects` and it does not expect
+// DesktopRegion to return an exact area for each rectangle in `rects`.
+template <template <typename, typename...> class T = std::initializer_list,
+ typename... Rect>
+void AssertUpdatedRegionIs(const DesktopFrame& frame,
+ const T<DesktopRect, Rect...>& rects) {
+ DesktopRegion region;
+ for (const auto& rect : rects) {
+ region.AddRect(rect);
+ }
+ ASSERT_TRUE(frame.updated_region().Equals(region));
+}
+
+// Compares and asserts `frame`.updated_region() covers all rectangles in
+// `rects`, but does not cover areas other than a kBlockSize expansion. This
+// function does not care about the order of the `rects`, and it does not expect
+// DesktopRegion to return an exact area of each rectangle in `rects`.
+template <template <typename, typename...> class T = std::initializer_list,
+ typename... Rect>
+void AssertUpdatedRegionCovers(const DesktopFrame& frame,
+ const T<DesktopRect, Rect...>& rects) {
+ DesktopRegion region;
+ for (const auto& rect : rects) {
+ region.AddRect(rect);
+ }
+
+ // Intersect of `rects` and `frame`.updated_region() should be `rects`. i.e.
+ // `frame`.updated_region() should be a superset of `rects`.
+ DesktopRegion intersect(region);
+ intersect.IntersectWith(frame.updated_region());
+ ASSERT_TRUE(region.Equals(intersect));
+
+ // Difference between `rects` and `frame`.updated_region() should not cover
+ // areas which have larger than twice of kBlockSize width and height.
+ //
+ // Explanation of the 'twice' of kBlockSize (indeed kBlockSize * 2 - 2) is
+ // following,
+ // (Each block in the following grid is a 8 x 8 pixels area. X means the real
+ // updated area, m means the updated area marked by
+ // DesktopCapturerDifferWrapper.)
+ // +---+---+---+---+---+---+---+---+
+ // | X | m | m | m | m | m | m | m |
+ // +---+---+---+---+---+---+---+---+
+ // | m | m | m | m | m | m | m | m |
+ // +---+---+---+---+---+---+---+---+
+ // | m | m | m | m | m | m | m | m |
+ // +---+---+---+---+---+---+---+---+
+ // | m | m | m | m | m | m | m | X |
+ // +---+---+---+---+---+---+---+---+
+ // The top left [0, 0] - [8, 8] and right bottom [56, 24] - [64, 32] blocks of
+ // this area are updated. But since DesktopCapturerDifferWrapper compares
+ // 32 x 32 blocks by default, this entire area is marked as updated. So the
+ // [8, 8] - [56, 32] is expected to be covered in the difference.
+ //
+ // But if [0, 0] - [8, 8] and [64, 24] - [72, 32] blocks are updated,
+ // +---+---+---+---+---+---+---+---+---+---+---+---+
+ // | X | m | m | m | | | | | m | m | m | m |
+ // +---+---+---+---+---+---+---+---+---+---+---+---+
+ // | m | m | m | m | | | | | m | m | m | m |
+ // +---+---+---+---+---+---+---+---+---+---+---+---+
+ // | m | m | m | m | | | | | m | m | m | m |
+ // +---+---+---+---+---+---+---+---+---+---+---+---+
+ // | m | m | m | m | | | | | X | m | m | m |
+ // +---+---+---+---+---+---+---+---+---+---+---+---+
+ // the [8, 8] - [64, 32] is not expected to be covered in the difference. As
+ // DesktopCapturerDifferWrapper should only mark [0, 0] - [32, 32] and
+ // [64, 0] - [96, 32] as updated.
+ DesktopRegion differ(frame.updated_region());
+ differ.Subtract(region);
+ for (DesktopRegion::Iterator it(differ); !it.IsAtEnd(); it.Advance()) {
+ ASSERT_TRUE(it.rect().width() <= kBlockSize * 2 - 2 ||
+ it.rect().height() <= kBlockSize * 2 - 2);
+ }
+}
+
+// Executes a DesktopCapturerDifferWrapper::Capture() and compares its output
+// DesktopFrame::updated_region() with `updated_region` if `check_result` is
+// true. If `exactly_match` is true, AssertUpdatedRegionIs() will be used,
+// otherwise AssertUpdatedRegionCovers() will be used.
+template <template <typename, typename...> class T = std::initializer_list,
+ typename... Rect>
+void ExecuteDifferWrapperCase(BlackWhiteDesktopFramePainter* frame_painter,
+ DesktopCapturerDifferWrapper* capturer,
+ MockDesktopCapturerCallback* callback,
+ const T<DesktopRect, Rect...>& updated_region,
+ bool check_result,
+ bool exactly_match) {
+ EXPECT_CALL(*callback, OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS,
+ ::testing::_))
+ .Times(1)
+ .WillOnce(
+ ::testing::Invoke([&updated_region, check_result, exactly_match](
+ DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame>* frame) {
+ ASSERT_EQ(result, DesktopCapturer::Result::SUCCESS);
+ if (check_result) {
+ if (exactly_match) {
+ AssertUpdatedRegionIs(**frame, updated_region);
+ } else {
+ AssertUpdatedRegionCovers(**frame, updated_region);
+ }
+ }
+ }));
+ for (const auto& rect : updated_region) {
+ frame_painter->updated_region()->AddRect(rect);
+ }
+ capturer->CaptureFrame();
+}
+
+// Executes a DesktopCapturerDifferWrapper::Capture(), if updated_region() is
+// not set, this function will reset DesktopCapturerDifferWrapper internal
+// DesktopFrame into black.
+void ExecuteCapturer(DesktopCapturerDifferWrapper* capturer,
+ MockDesktopCapturerCallback* callback) {
+ EXPECT_CALL(*callback, OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS,
+ ::testing::_))
+ .Times(1);
+ capturer->CaptureFrame();
+}
+
+void ExecuteDifferWrapperTest(bool with_hints,
+ bool enlarge_updated_region,
+ bool random_updated_region,
+ bool check_result) {
+ const bool updated_region_should_exactly_match =
+ with_hints && !enlarge_updated_region && !random_updated_region;
+ BlackWhiteDesktopFramePainter frame_painter;
+ PainterDesktopFrameGenerator frame_generator;
+ frame_generator.set_desktop_frame_painter(&frame_painter);
+ std::unique_ptr<FakeDesktopCapturer> fake(new FakeDesktopCapturer());
+ fake->set_frame_generator(&frame_generator);
+ DesktopCapturerDifferWrapper capturer(std::move(fake));
+ MockDesktopCapturerCallback callback;
+ frame_generator.set_provide_updated_region_hints(with_hints);
+ frame_generator.set_enlarge_updated_region(enlarge_updated_region);
+ frame_generator.set_add_random_updated_region(random_updated_region);
+
+ capturer.Start(&callback);
+
+ EXPECT_CALL(callback, OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS,
+ ::testing::_))
+ .Times(1)
+ .WillOnce(::testing::Invoke([](DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame>* frame) {
+ ASSERT_EQ(result, DesktopCapturer::Result::SUCCESS);
+ AssertUpdatedRegionIs(**frame,
+ {DesktopRect::MakeSize((*frame)->size())});
+ }));
+ capturer.CaptureFrame();
+
+ ExecuteDifferWrapperCase(&frame_painter, &capturer, &callback,
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(300, 300, 400, 400)},
+ check_result, updated_region_should_exactly_match);
+ ExecuteCapturer(&capturer, &callback);
+
+ ExecuteDifferWrapperCase(
+ &frame_painter, &capturer, &callback,
+ {DesktopRect::MakeLTRB(0, 0, 40, 40),
+ DesktopRect::MakeLTRB(0, frame_generator.size()->height() - 40, 40,
+ frame_generator.size()->height()),
+ DesktopRect::MakeLTRB(frame_generator.size()->width() - 40, 0,
+ frame_generator.size()->width(), 40),
+ DesktopRect::MakeLTRB(frame_generator.size()->width() - 40,
+ frame_generator.size()->height() - 40,
+ frame_generator.size()->width(),
+ frame_generator.size()->height())},
+ check_result, updated_region_should_exactly_match);
+
+ Random random(rtc::TimeMillis());
+ // Fuzzing tests.
+ for (int i = 0; i < 1000; i++) {
+ if (enlarge_updated_region) {
+ frame_generator.set_enlarge_range(random.Rand(1, 50));
+ }
+ frame_generator.size()->set(random.Rand(500, 2000), random.Rand(500, 2000));
+ ExecuteCapturer(&capturer, &callback);
+ std::vector<DesktopRect> updated_region;
+ for (int j = random.Rand(50); j >= 0; j--) {
+ // At least a 1 x 1 updated region.
+ const int left = random.Rand(0, frame_generator.size()->width() - 2);
+ const int top = random.Rand(0, frame_generator.size()->height() - 2);
+ const int right = random.Rand(left + 1, frame_generator.size()->width());
+ const int bottom = random.Rand(top + 1, frame_generator.size()->height());
+ updated_region.push_back(DesktopRect::MakeLTRB(left, top, right, bottom));
+ }
+ ExecuteDifferWrapperCase(&frame_painter, &capturer, &callback,
+ updated_region, check_result,
+ updated_region_should_exactly_match);
+ }
+}
+
+} // namespace
+
+TEST(DesktopCapturerDifferWrapperTest, CaptureWithoutHints) {
+ ExecuteDifferWrapperTest(false, false, false, true);
+}
+
+TEST(DesktopCapturerDifferWrapperTest, CaptureWithHints) {
+ ExecuteDifferWrapperTest(true, false, false, true);
+}
+
+TEST(DesktopCapturerDifferWrapperTest, CaptureWithEnlargedHints) {
+ ExecuteDifferWrapperTest(true, true, false, true);
+}
+
+TEST(DesktopCapturerDifferWrapperTest, CaptureWithRandomHints) {
+ ExecuteDifferWrapperTest(true, false, true, true);
+}
+
+TEST(DesktopCapturerDifferWrapperTest, CaptureWithEnlargedAndRandomHints) {
+ ExecuteDifferWrapperTest(true, true, true, true);
+}
+
+// When hints are provided, DesktopCapturerDifferWrapper has a slightly better
+// performance in current configuration, but not so significant. Following is
+// one run result.
+// [ RUN ] DISABLED_CaptureWithoutHintsPerf
+// [ OK ] DISABLED_CaptureWithoutHintsPerf (7118 ms)
+// [ RUN ] DISABLED_CaptureWithHintsPerf
+// [ OK ] DISABLED_CaptureWithHintsPerf (5580 ms)
+// [ RUN ] DISABLED_CaptureWithEnlargedHintsPerf
+// [ OK ] DISABLED_CaptureWithEnlargedHintsPerf (5974 ms)
+// [ RUN ] DISABLED_CaptureWithRandomHintsPerf
+// [ OK ] DISABLED_CaptureWithRandomHintsPerf (6184 ms)
+// [ RUN ] DISABLED_CaptureWithEnlargedAndRandomHintsPerf
+// [ OK ] DISABLED_CaptureWithEnlargedAndRandomHintsPerf (6347 ms)
+TEST(DesktopCapturerDifferWrapperTest, DISABLED_CaptureWithoutHintsPerf) {
+ int64_t started = rtc::TimeMillis();
+ ExecuteDifferWrapperTest(false, false, false, false);
+ ASSERT_LE(rtc::TimeMillis() - started, 15000);
+}
+
+TEST(DesktopCapturerDifferWrapperTest, DISABLED_CaptureWithHintsPerf) {
+ int64_t started = rtc::TimeMillis();
+ ExecuteDifferWrapperTest(true, false, false, false);
+ ASSERT_LE(rtc::TimeMillis() - started, 15000);
+}
+
+TEST(DesktopCapturerDifferWrapperTest, DISABLED_CaptureWithEnlargedHintsPerf) {
+ int64_t started = rtc::TimeMillis();
+ ExecuteDifferWrapperTest(true, true, false, false);
+ ASSERT_LE(rtc::TimeMillis() - started, 15000);
+}
+
+TEST(DesktopCapturerDifferWrapperTest, DISABLED_CaptureWithRandomHintsPerf) {
+ int64_t started = rtc::TimeMillis();
+ ExecuteDifferWrapperTest(true, false, true, false);
+ ASSERT_LE(rtc::TimeMillis() - started, 15000);
+}
+
+TEST(DesktopCapturerDifferWrapperTest,
+ DISABLED_CaptureWithEnlargedAndRandomHintsPerf) {
+ int64_t started = rtc::TimeMillis();
+ ExecuteDifferWrapperTest(true, true, true, false);
+ ASSERT_LE(rtc::TimeMillis() - started, 15000);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_wrapper.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_wrapper.cc
new file mode 100644
index 0000000000..4bbdd6c94f
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_wrapper.cc
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_capturer_wrapper.h"
+
+#include <utility>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+DesktopCapturerWrapper::DesktopCapturerWrapper(
+ std::unique_ptr<DesktopCapturer> base_capturer)
+ : base_capturer_(std::move(base_capturer)) {
+ RTC_DCHECK(base_capturer_);
+}
+
+DesktopCapturerWrapper::~DesktopCapturerWrapper() = default;
+
+void DesktopCapturerWrapper::Start(Callback* callback) {
+ base_capturer_->Start(callback);
+}
+
+void DesktopCapturerWrapper::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
+ base_capturer_->SetSharedMemoryFactory(std::move(shared_memory_factory));
+}
+
+void DesktopCapturerWrapper::CaptureFrame() {
+ base_capturer_->CaptureFrame();
+}
+
+void DesktopCapturerWrapper::SetExcludedWindow(WindowId window) {
+ base_capturer_->SetExcludedWindow(window);
+}
+
+bool DesktopCapturerWrapper::GetSourceList(SourceList* sources) {
+ return base_capturer_->GetSourceList(sources);
+}
+
+bool DesktopCapturerWrapper::SelectSource(SourceId id) {
+ return base_capturer_->SelectSource(id);
+}
+
+bool DesktopCapturerWrapper::FocusOnSelectedSource() {
+ return base_capturer_->FocusOnSelectedSource();
+}
+
+bool DesktopCapturerWrapper::IsOccluded(const DesktopVector& pos) {
+ return base_capturer_->IsOccluded(pos);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_wrapper.h b/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_wrapper.h
new file mode 100644
index 0000000000..e0f50d79e1
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_capturer_wrapper.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURER_WRAPPER_H_
+#define MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURER_WRAPPER_H_
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/shared_memory.h"
+
+namespace webrtc {
+
+// Wraps a DesktopCapturer and forwards all the function calls to it.
+class DesktopCapturerWrapper : public DesktopCapturer {
+ public:
+ explicit DesktopCapturerWrapper(
+ std::unique_ptr<DesktopCapturer> base_capturer);
+ ~DesktopCapturerWrapper() override;
+
+ // DesktopCapturer implementations.
+ void Start(Callback* callback) override;
+ void SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) override;
+ void CaptureFrame() override;
+ void SetExcludedWindow(WindowId window) override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+ bool FocusOnSelectedSource() override;
+ bool IsOccluded(const DesktopVector& pos) override;
+
+ protected:
+ // Guaranteed to be valid.
+ const std::unique_ptr<DesktopCapturer> base_capturer_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURER_WRAPPER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_frame.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_frame.cc
new file mode 100644
index 0000000000..3e7b4770c6
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_frame.cc
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_frame.h"
+
+#include <string.h>
+
+#include <cmath>
+#include <memory>
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "rtc_base/checks.h"
+#include "libyuv/include/libyuv/planar_functions.h"
+
+namespace webrtc {
+
+DesktopFrame::DesktopFrame(DesktopSize size,
+ int stride,
+ uint8_t* data,
+ SharedMemory* shared_memory)
+ : data_(data),
+ shared_memory_(shared_memory),
+ size_(size),
+ stride_(stride),
+ capture_time_ms_(0),
+ capturer_id_(DesktopCapturerId::kUnknown) {
+ RTC_DCHECK(size_.width() >= 0);
+ RTC_DCHECK(size_.height() >= 0);
+}
+
+DesktopFrame::~DesktopFrame() = default;
+
+void DesktopFrame::CopyPixelsFrom(const uint8_t* src_buffer,
+ int src_stride,
+ const DesktopRect& dest_rect) {
+ RTC_CHECK(DesktopRect::MakeSize(size()).ContainsRect(dest_rect));
+
+ uint8_t* dest = GetFrameDataAtPos(dest_rect.top_left());
+ // TODO(crbug.com/1330019): Temporary workaround for a known libyuv crash when
+ // the height or width is 0. Remove this once this change has been merged.
+ if (dest_rect.width() && dest_rect.height()) {
+ libyuv::CopyPlane(src_buffer, src_stride, dest, stride(),
+ DesktopFrame::kBytesPerPixel * dest_rect.width(),
+ dest_rect.height());
+ }
+}
+
+void DesktopFrame::CopyPixelsFrom(const DesktopFrame& src_frame,
+ const DesktopVector& src_pos,
+ const DesktopRect& dest_rect) {
+ RTC_CHECK(DesktopRect::MakeSize(src_frame.size())
+ .ContainsRect(
+ DesktopRect::MakeOriginSize(src_pos, dest_rect.size())));
+
+ CopyPixelsFrom(src_frame.GetFrameDataAtPos(src_pos), src_frame.stride(),
+ dest_rect);
+}
+
+bool DesktopFrame::CopyIntersectingPixelsFrom(const DesktopFrame& src_frame,
+ double horizontal_scale,
+ double vertical_scale) {
+ const DesktopVector& origin = top_left();
+ const DesktopVector& src_frame_origin = src_frame.top_left();
+
+ DesktopVector src_frame_offset = src_frame_origin.subtract(origin);
+
+ // Determine the intersection, first adjusting its origin to account for any
+ // DPI scaling.
+ DesktopRect intersection_rect = src_frame.rect();
+ if (horizontal_scale != 1.0 || vertical_scale != 1.0) {
+ DesktopVector origin_adjustment(
+ static_cast<int>(
+ std::round((horizontal_scale - 1.0) * src_frame_offset.x())),
+ static_cast<int>(
+ std::round((vertical_scale - 1.0) * src_frame_offset.y())));
+
+ intersection_rect.Translate(origin_adjustment);
+
+ src_frame_offset = src_frame_offset.add(origin_adjustment);
+ }
+
+ intersection_rect.IntersectWith(rect());
+ if (intersection_rect.is_empty()) {
+ return false;
+ }
+
+ // Translate the intersection rect to be relative to the outer rect.
+ intersection_rect.Translate(-origin.x(), -origin.y());
+
+ // Determine source position for the copy (offsets of outer frame from
+ // source origin, if positive).
+ int32_t src_pos_x = std::max(0, -src_frame_offset.x());
+ int32_t src_pos_y = std::max(0, -src_frame_offset.y());
+
+ CopyPixelsFrom(src_frame, DesktopVector(src_pos_x, src_pos_y),
+ intersection_rect);
+ return true;
+}
+
+DesktopRect DesktopFrame::rect() const {
+ const float scale = scale_factor();
+ // Only scale the size.
+ return DesktopRect::MakeXYWH(top_left().x(), top_left().y(),
+ size().width() / scale, size().height() / scale);
+}
+
+float DesktopFrame::scale_factor() const {
+ float scale = 1.0f;
+
+#if defined(WEBRTC_MAC) || defined(CHROMEOS)
+ // At least on Windows the logical and physical pixel are the same
+ // See http://crbug.com/948362.
+ if (!dpi().is_zero() && dpi().x() == dpi().y())
+ scale = dpi().x() / kStandardDPI;
+#endif
+
+ return scale;
+}
+
+uint8_t* DesktopFrame::GetFrameDataAtPos(const DesktopVector& pos) const {
+ return data() + stride() * pos.y() + DesktopFrame::kBytesPerPixel * pos.x();
+}
+
+void DesktopFrame::CopyFrameInfoFrom(const DesktopFrame& other) {
+ set_dpi(other.dpi());
+ set_capture_time_ms(other.capture_time_ms());
+ set_capturer_id(other.capturer_id());
+ *mutable_updated_region() = other.updated_region();
+ set_top_left(other.top_left());
+ set_icc_profile(other.icc_profile());
+}
+
+void DesktopFrame::MoveFrameInfoFrom(DesktopFrame* other) {
+ set_dpi(other->dpi());
+ set_capture_time_ms(other->capture_time_ms());
+ set_capturer_id(other->capturer_id());
+ mutable_updated_region()->Swap(other->mutable_updated_region());
+ set_top_left(other->top_left());
+ set_icc_profile(other->icc_profile());
+}
+
+BasicDesktopFrame::BasicDesktopFrame(DesktopSize size)
+ : DesktopFrame(size,
+ kBytesPerPixel * size.width(),
+ new uint8_t[kBytesPerPixel * size.width() * size.height()](),
+ nullptr) {}
+
+BasicDesktopFrame::~BasicDesktopFrame() {
+ delete[] data_;
+}
+
+// static
+DesktopFrame* BasicDesktopFrame::CopyOf(const DesktopFrame& frame) {
+ DesktopFrame* result = new BasicDesktopFrame(frame.size());
+ // TODO(crbug.com/1330019): Temporary workaround for a known libyuv crash when
+ // the height or width is 0. Remove this once this change has been merged.
+ if (frame.size().width() && frame.size().height()) {
+ libyuv::CopyPlane(frame.data(), frame.stride(), result->data(),
+ result->stride(), frame.size().width() * kBytesPerPixel,
+ frame.size().height());
+ }
+ result->CopyFrameInfoFrom(frame);
+ return result;
+}
+
+// static
+std::unique_ptr<DesktopFrame> SharedMemoryDesktopFrame::Create(
+ DesktopSize size,
+ SharedMemoryFactory* shared_memory_factory) {
+ RTC_DCHECK(shared_memory_factory);
+
+ size_t buffer_size = size.height() * size.width() * kBytesPerPixel;
+ std::unique_ptr<SharedMemory> shared_memory =
+ shared_memory_factory->CreateSharedMemory(buffer_size);
+ if (!shared_memory)
+ return nullptr;
+
+ return std::make_unique<SharedMemoryDesktopFrame>(
+ size, size.width() * kBytesPerPixel, std::move(shared_memory));
+}
+
+SharedMemoryDesktopFrame::SharedMemoryDesktopFrame(DesktopSize size,
+ int stride,
+ SharedMemory* shared_memory)
+ : DesktopFrame(size,
+ stride,
+ reinterpret_cast<uint8_t*>(shared_memory->data()),
+ shared_memory) {}
+
+SharedMemoryDesktopFrame::SharedMemoryDesktopFrame(
+ DesktopSize size,
+ int stride,
+ std::unique_ptr<SharedMemory> shared_memory)
+ : SharedMemoryDesktopFrame(size, stride, shared_memory.release()) {}
+
+SharedMemoryDesktopFrame::~SharedMemoryDesktopFrame() {
+ delete shared_memory_;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_frame.h b/third_party/libwebrtc/modules/desktop_capture/desktop_frame.h
new file mode 100644
index 0000000000..3ee1867e70
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_frame.h
@@ -0,0 +1,227 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_H_
+#define MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_H_
+
+#include <stdint.h>
+
+#include <memory>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/shared_memory.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+const float kStandardDPI = 96.0f;
+
+// DesktopFrame represents a video frame captured from the screen.
+class RTC_EXPORT DesktopFrame {
+ public:
+ // DesktopFrame objects always hold BGRA data.
+ static const int kBytesPerPixel = 4;
+
+ virtual ~DesktopFrame();
+
+ DesktopFrame(const DesktopFrame&) = delete;
+ DesktopFrame& operator=(const DesktopFrame&) = delete;
+
+ // Returns the rectangle in full desktop coordinates to indicate it covers
+ // the area of top_left() to top_letf() + size() / scale_factor().
+ DesktopRect rect() const;
+
+ // Returns the scale factor from DIPs to physical pixels of the frame.
+ // Assumes same scale in both X and Y directions at present.
+ float scale_factor() const;
+
+ // Size of the frame. In physical coordinates, mapping directly from the
+ // underlying buffer.
+ const DesktopSize& size() const { return size_; }
+
+ // The top-left of the frame in full desktop coordinates. E.g. the top left
+ // monitor should start from (0, 0). The desktop coordinates may be scaled by
+ // OS, but this is always consistent with the MouseCursorMonitor.
+ const DesktopVector& top_left() const { return top_left_; }
+ void set_top_left(const DesktopVector& top_left) { top_left_ = top_left; }
+
+ // Distance in the buffer between two neighboring rows in bytes.
+ int stride() const { return stride_; }
+
+ // Data buffer used for the frame.
+ uint8_t* data() const { return data_; }
+
+ // SharedMemory used for the buffer or NULL if memory is allocated on the
+ // heap. The result is guaranteed to be deleted only after the frame is
+ // deleted (classes that inherit from DesktopFrame must ensure it).
+ SharedMemory* shared_memory() const { return shared_memory_; }
+
+ // Indicates region of the screen that has changed since the previous frame.
+ const DesktopRegion& updated_region() const { return updated_region_; }
+ DesktopRegion* mutable_updated_region() { return &updated_region_; }
+
+ // DPI of the screen being captured. May be set to zero, e.g. if DPI is
+ // unknown.
+ const DesktopVector& dpi() const { return dpi_; }
+ void set_dpi(const DesktopVector& dpi) { dpi_ = dpi; }
+
+ // Indicates if this frame may have the mouse cursor in it. Capturers that
+ // support cursor capture may set this to true. If the cursor was
+ // outside of the captured area, this may be true even though the cursor is
+ // not in the image.
+ bool may_contain_cursor() const { return may_contain_cursor_; }
+ void set_may_contain_cursor(bool may_contain_cursor) {
+ may_contain_cursor_ = may_contain_cursor;
+ }
+
+ // Time taken to capture the frame in milliseconds.
+ int64_t capture_time_ms() const { return capture_time_ms_; }
+ void set_capture_time_ms(int64_t time_ms) { capture_time_ms_ = time_ms; }
+
+ // Copies pixels from a buffer or another frame. `dest_rect` rect must lay
+ // within bounds of this frame.
+ void CopyPixelsFrom(const uint8_t* src_buffer,
+ int src_stride,
+ const DesktopRect& dest_rect);
+ void CopyPixelsFrom(const DesktopFrame& src_frame,
+ const DesktopVector& src_pos,
+ const DesktopRect& dest_rect);
+
+ // Copies pixels from another frame, with the copied & overwritten regions
+ // representing the intersection between the two frames. Returns true if
+ // pixels were copied, or false if there's no intersection. The scale factors
+ // represent the ratios between pixel space & offset coordinate space (e.g.
+ // 2.0 would indicate the frames are scaled down by 50% for display, so any
+ // offset between their origins should be doubled).
+ bool CopyIntersectingPixelsFrom(const DesktopFrame& src_frame,
+ double horizontal_scale,
+ double vertical_scale);
+
+ // A helper to return the data pointer of a frame at the specified position.
+ uint8_t* GetFrameDataAtPos(const DesktopVector& pos) const;
+
+ // The DesktopCapturer implementation which generates current DesktopFrame.
+ // Not all DesktopCapturer implementations set this field; it's set to
+ // kUnknown by default.
+ uint32_t capturer_id() const { return capturer_id_; }
+ void set_capturer_id(uint32_t capturer_id) { capturer_id_ = capturer_id; }
+
+ // Copies various information from `other`. Anything initialized in
+ // constructor are not copied.
+ // This function is usually used when sharing a source DesktopFrame with
+ // several clients: the original DesktopFrame should be kept unchanged. For
+ // example, BasicDesktopFrame::CopyOf() and SharedDesktopFrame::Share().
+ void CopyFrameInfoFrom(const DesktopFrame& other);
+
+ // Copies various information from `other`. Anything initialized in
+ // constructor are not copied. Not like CopyFrameInfoFrom() function, this
+ // function uses swap or move constructor to avoid data copy. It won't break
+ // the `other`, but some of its information may be missing after this
+ // operation. E.g. other->updated_region_;
+ // This function is usually used when wrapping a DesktopFrame: the wrapper
+ // instance takes the ownership of `other`, so other components cannot access
+ // `other` anymore. For example, CroppedDesktopFrame and
+ // DesktopFrameWithCursor.
+ void MoveFrameInfoFrom(DesktopFrame* other);
+
+ // Set and get the ICC profile of the frame data pixels. Useful to build the
+ // a ColorSpace object from clients of webrtc library like chromium. The
+ // format of an ICC profile is defined in the following specification
+ // http://www.color.org/specification/ICC1v43_2010-12.pdf.
+ const std::vector<uint8_t>& icc_profile() const { return icc_profile_; }
+ void set_icc_profile(const std::vector<uint8_t>& icc_profile) {
+ icc_profile_ = icc_profile;
+ }
+
+ protected:
+ DesktopFrame(DesktopSize size,
+ int stride,
+ uint8_t* data,
+ SharedMemory* shared_memory);
+
+ // Ownership of the buffers is defined by the classes that inherit from this
+ // class. They must guarantee that the buffer is not deleted before the frame
+ // is deleted.
+ uint8_t* const data_;
+ SharedMemory* const shared_memory_;
+
+ private:
+ const DesktopSize size_;
+ const int stride_;
+
+ DesktopRegion updated_region_;
+ DesktopVector top_left_;
+ DesktopVector dpi_;
+ bool may_contain_cursor_ = false;
+ int64_t capture_time_ms_;
+ uint32_t capturer_id_;
+ std::vector<uint8_t> icc_profile_;
+};
+
+// A DesktopFrame that stores data in the heap.
+class RTC_EXPORT BasicDesktopFrame : public DesktopFrame {
+ public:
+ // The entire data buffer used for the frame is initialized with zeros.
+ explicit BasicDesktopFrame(DesktopSize size);
+
+ ~BasicDesktopFrame() override;
+
+ BasicDesktopFrame(const BasicDesktopFrame&) = delete;
+ BasicDesktopFrame& operator=(const BasicDesktopFrame&) = delete;
+
+ // Creates a BasicDesktopFrame that contains copy of `frame`.
+ // TODO(zijiehe): Return std::unique_ptr<DesktopFrame>
+ static DesktopFrame* CopyOf(const DesktopFrame& frame);
+};
+
+// A DesktopFrame that stores data in shared memory.
+class RTC_EXPORT SharedMemoryDesktopFrame : public DesktopFrame {
+ public:
+ // May return nullptr if `shared_memory_factory` failed to create a
+ // SharedMemory instance.
+ // `shared_memory_factory` should not be nullptr.
+ static std::unique_ptr<DesktopFrame> Create(
+ DesktopSize size,
+ SharedMemoryFactory* shared_memory_factory);
+
+ // Takes ownership of `shared_memory`.
+ // Deprecated, use the next constructor.
+ SharedMemoryDesktopFrame(DesktopSize size,
+ int stride,
+ SharedMemory* shared_memory);
+
+ // Preferred.
+ SharedMemoryDesktopFrame(DesktopSize size,
+ int stride,
+ std::unique_ptr<SharedMemory> shared_memory);
+
+ ~SharedMemoryDesktopFrame() override;
+
+ SharedMemoryDesktopFrame(const SharedMemoryDesktopFrame&) = delete;
+ SharedMemoryDesktopFrame& operator=(const SharedMemoryDesktopFrame&) = delete;
+
+ private:
+ // Avoid unexpected order of parameter evaluation.
+ // Executing both std::unique_ptr<T>::operator->() and
+ // std::unique_ptr<T>::release() in the member initializer list is not safe.
+ // Depends on the order of parameter evaluation,
+ // std::unique_ptr<T>::operator->() may trigger assertion failure if it has
+ // been evaluated after std::unique_ptr<T>::release(). By using this
+ // constructor, std::unique_ptr<T>::operator->() won't be involved anymore.
+ SharedMemoryDesktopFrame(DesktopRect rect,
+ int stride,
+ SharedMemory* shared_memory);
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_frame_generator.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_generator.cc
new file mode 100644
index 0000000000..b5dfc28e46
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_generator.cc
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_frame_generator.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/rgba_color.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/random.h"
+#include "rtc_base/time_utils.h"
+
+namespace webrtc {
+
+namespace {
+
+// Sets `updated_region` to `frame`. If `enlarge_updated_region` is
+// true, this function will randomly enlarge each DesktopRect in
+// `updated_region`. But the enlarged DesktopRegion won't excceed the
+// frame->size(). If `add_random_updated_region` is true, several random
+// rectangles will also be included in `frame`.
+void SetUpdatedRegion(DesktopFrame* frame,
+ const DesktopRegion& updated_region,
+ bool enlarge_updated_region,
+ int enlarge_range,
+ bool add_random_updated_region) {
+ const DesktopRect screen_rect = DesktopRect::MakeSize(frame->size());
+ Random random(rtc::TimeMicros());
+ frame->mutable_updated_region()->Clear();
+ for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
+ it.Advance()) {
+ DesktopRect rect = it.rect();
+ if (enlarge_updated_region && enlarge_range > 0) {
+ rect.Extend(random.Rand(enlarge_range), random.Rand(enlarge_range),
+ random.Rand(enlarge_range), random.Rand(enlarge_range));
+ rect.IntersectWith(screen_rect);
+ }
+ frame->mutable_updated_region()->AddRect(rect);
+ }
+
+ if (add_random_updated_region) {
+ for (int i = random.Rand(10); i >= 0; i--) {
+ // At least a 1 x 1 updated region.
+ const int left = random.Rand(0, frame->size().width() - 2);
+ const int top = random.Rand(0, frame->size().height() - 2);
+ const int right = random.Rand(left + 1, frame->size().width());
+ const int bottom = random.Rand(top + 1, frame->size().height());
+ frame->mutable_updated_region()->AddRect(
+ DesktopRect::MakeLTRB(left, top, right, bottom));
+ }
+ }
+}
+
+// Paints pixels in `rect` of `frame` to `color`.
+void PaintRect(DesktopFrame* frame, DesktopRect rect, RgbaColor rgba_color) {
+ static_assert(DesktopFrame::kBytesPerPixel == sizeof(uint32_t),
+ "kBytesPerPixel should be 4.");
+ RTC_DCHECK_GE(frame->size().width(), rect.right());
+ RTC_DCHECK_GE(frame->size().height(), rect.bottom());
+ uint32_t color = rgba_color.ToUInt32();
+ uint8_t* row = frame->GetFrameDataAtPos(rect.top_left());
+ for (int i = 0; i < rect.height(); i++) {
+ uint32_t* column = reinterpret_cast<uint32_t*>(row);
+ for (int j = 0; j < rect.width(); j++) {
+ column[j] = color;
+ }
+ row += frame->stride();
+ }
+}
+
+// Paints pixels in `region` of `frame` to `color`.
+void PaintRegion(DesktopFrame* frame,
+ DesktopRegion* region,
+ RgbaColor rgba_color) {
+ region->IntersectWith(DesktopRect::MakeSize(frame->size()));
+ for (DesktopRegion::Iterator it(*region); !it.IsAtEnd(); it.Advance()) {
+ PaintRect(frame, it.rect(), rgba_color);
+ }
+}
+
+} // namespace
+
+DesktopFrameGenerator::DesktopFrameGenerator() {}
+DesktopFrameGenerator::~DesktopFrameGenerator() {}
+
+DesktopFramePainter::DesktopFramePainter() {}
+DesktopFramePainter::~DesktopFramePainter() {}
+
+PainterDesktopFrameGenerator::PainterDesktopFrameGenerator()
+ : size_(1024, 768),
+ return_frame_(true),
+ provide_updated_region_hints_(false),
+ enlarge_updated_region_(false),
+ enlarge_range_(20),
+ add_random_updated_region_(false),
+ painter_(nullptr) {}
+PainterDesktopFrameGenerator::~PainterDesktopFrameGenerator() {}
+
+std::unique_ptr<DesktopFrame> PainterDesktopFrameGenerator::GetNextFrame(
+ SharedMemoryFactory* factory) {
+ if (!return_frame_) {
+ return nullptr;
+ }
+
+ std::unique_ptr<DesktopFrame> frame = std::unique_ptr<DesktopFrame>(
+ factory ? SharedMemoryDesktopFrame::Create(size_, factory).release()
+ : new BasicDesktopFrame(size_));
+ if (painter_) {
+ DesktopRegion updated_region;
+ if (!painter_->Paint(frame.get(), &updated_region)) {
+ return nullptr;
+ }
+
+ if (provide_updated_region_hints_) {
+ SetUpdatedRegion(frame.get(), updated_region, enlarge_updated_region_,
+ enlarge_range_, add_random_updated_region_);
+ } else {
+ frame->mutable_updated_region()->SetRect(
+ DesktopRect::MakeSize(frame->size()));
+ }
+ }
+
+ return frame;
+}
+
+DesktopSize* PainterDesktopFrameGenerator::size() {
+ return &size_;
+}
+
+void PainterDesktopFrameGenerator::set_return_frame(bool return_frame) {
+ return_frame_ = return_frame;
+}
+
+void PainterDesktopFrameGenerator::set_provide_updated_region_hints(
+ bool provide_updated_region_hints) {
+ provide_updated_region_hints_ = provide_updated_region_hints;
+}
+
+void PainterDesktopFrameGenerator::set_enlarge_updated_region(
+ bool enlarge_updated_region) {
+ enlarge_updated_region_ = enlarge_updated_region;
+}
+
+void PainterDesktopFrameGenerator::set_enlarge_range(int enlarge_range) {
+ enlarge_range_ = enlarge_range;
+}
+
+void PainterDesktopFrameGenerator::set_add_random_updated_region(
+ bool add_random_updated_region) {
+ add_random_updated_region_ = add_random_updated_region;
+}
+
+void PainterDesktopFrameGenerator::set_desktop_frame_painter(
+ DesktopFramePainter* painter) {
+ painter_ = painter;
+}
+
+BlackWhiteDesktopFramePainter::BlackWhiteDesktopFramePainter() {}
+BlackWhiteDesktopFramePainter::~BlackWhiteDesktopFramePainter() {}
+
+DesktopRegion* BlackWhiteDesktopFramePainter::updated_region() {
+ return &updated_region_;
+}
+
+bool BlackWhiteDesktopFramePainter::Paint(DesktopFrame* frame,
+ DesktopRegion* updated_region) {
+ RTC_DCHECK(updated_region->is_empty());
+ memset(frame->data(), 0, frame->stride() * frame->size().height());
+ PaintRegion(frame, &updated_region_, RgbaColor(0xFFFFFFFF));
+ updated_region_.Swap(updated_region);
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_frame_generator.h b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_generator.h
new file mode 100644
index 0000000000..3dedee9344
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_generator.h
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_GENERATOR_H_
+#define MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_GENERATOR_H_
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/shared_memory.h"
+
+namespace webrtc {
+
+// An interface to generate a DesktopFrame.
+class DesktopFrameGenerator {
+ public:
+ DesktopFrameGenerator();
+ virtual ~DesktopFrameGenerator();
+
+ virtual std::unique_ptr<DesktopFrame> GetNextFrame(
+ SharedMemoryFactory* factory) = 0;
+};
+
+// An interface to paint a DesktopFrame. This interface is used by
+// PainterDesktopFrameGenerator.
+class DesktopFramePainter {
+ public:
+ DesktopFramePainter();
+ virtual ~DesktopFramePainter();
+
+ virtual bool Paint(DesktopFrame* frame, DesktopRegion* updated_region) = 0;
+};
+
+// An implementation of DesktopFrameGenerator to take care about the
+// DesktopFrame size, filling updated_region(), etc, but leaves the real
+// painting work to a DesktopFramePainter implementation.
+class PainterDesktopFrameGenerator final : public DesktopFrameGenerator {
+ public:
+ PainterDesktopFrameGenerator();
+ ~PainterDesktopFrameGenerator() override;
+
+ std::unique_ptr<DesktopFrame> GetNextFrame(
+ SharedMemoryFactory* factory) override;
+
+ // Sets the size of the frame which will be returned in next GetNextFrame()
+ // call.
+ DesktopSize* size();
+
+ // Decides whether BaseDesktopFrameGenerator returns a frame in next Capture()
+ // callback. If return_frame_ is true, BaseDesktopFrameGenerator will create a
+ // frame according to both size_ and SharedMemoryFactory input, and uses
+ // Paint() function to paint it.
+ void set_return_frame(bool return_frame);
+
+ // Decides whether MockScreenCapturer returns a frame with updated regions.
+ // MockScreenCapturer will keep DesktopFrame::updated_region() empty if this
+ // field is false.
+ void set_provide_updated_region_hints(bool provide_updated_region_hints);
+
+ // Decides whether MockScreenCapturer randomly enlarges updated regions in the
+ // DesktopFrame. Set this field to true to simulate an inaccurate updated
+ // regions' return from OS APIs.
+ void set_enlarge_updated_region(bool enlarge_updated_region);
+
+ // The range to enlarge a updated region if `enlarge_updated_region_` is true.
+ // If this field is less than zero, it will be treated as zero, and
+ // `enlarge_updated_region_` will be ignored.
+ void set_enlarge_range(int enlarge_range);
+
+ // Decides whether BaseDesktopFrameGenerator randomly add some updated regions
+ // in the DesktopFrame. Set this field to true to simulate an inaccurate
+ // updated regions' return from OS APIs.
+ void set_add_random_updated_region(bool add_random_updated_region);
+
+ // Sets the painter object to do the real painting work, if no `painter_` has
+ // been set to this instance, the DesktopFrame returned by GetNextFrame()
+ // function will keep in an undefined but valid state.
+ // PainterDesktopFrameGenerator does not take ownership of the `painter`.
+ void set_desktop_frame_painter(DesktopFramePainter* painter);
+
+ private:
+ DesktopSize size_;
+ bool return_frame_;
+ bool provide_updated_region_hints_;
+ bool enlarge_updated_region_;
+ int enlarge_range_;
+ bool add_random_updated_region_;
+ DesktopFramePainter* painter_;
+};
+
+// An implementation of DesktopFramePainter to paint black on
+// mutable_updated_region(), and white elsewhere.
+class BlackWhiteDesktopFramePainter final : public DesktopFramePainter {
+ public:
+ BlackWhiteDesktopFramePainter();
+ ~BlackWhiteDesktopFramePainter() override;
+
+ // The black regions of the frame which will be returned in next Paint()
+ // call. BlackWhiteDesktopFramePainter will draw a white frame, with black
+ // in the updated_region_. Each Paint() call will consume updated_region_.
+ DesktopRegion* updated_region();
+
+ // DesktopFramePainter interface.
+ bool Paint(DesktopFrame* frame, DesktopRegion* updated_region) override;
+
+ private:
+ DesktopRegion updated_region_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_GENERATOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_frame_rotation.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_rotation.cc
new file mode 100644
index 0000000000..6e4e42708e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_rotation.cc
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_frame_rotation.h"
+
+#include "rtc_base/checks.h"
+#include "libyuv/include/libyuv/rotate_argb.h"
+
+namespace webrtc {
+
+namespace {
+
+libyuv::RotationMode ToLibyuvRotationMode(Rotation rotation) {
+ switch (rotation) {
+ case Rotation::CLOCK_WISE_0:
+ return libyuv::kRotate0;
+ case Rotation::CLOCK_WISE_90:
+ return libyuv::kRotate90;
+ case Rotation::CLOCK_WISE_180:
+ return libyuv::kRotate180;
+ case Rotation::CLOCK_WISE_270:
+ return libyuv::kRotate270;
+ }
+ RTC_DCHECK_NOTREACHED();
+ return libyuv::kRotate0;
+}
+
+DesktopRect RotateAndOffsetRect(DesktopRect rect,
+ DesktopSize size,
+ Rotation rotation,
+ DesktopVector offset) {
+ DesktopRect result = RotateRect(rect, size, rotation);
+ result.Translate(offset);
+ return result;
+}
+
+} // namespace
+
+Rotation ReverseRotation(Rotation rotation) {
+ switch (rotation) {
+ case Rotation::CLOCK_WISE_0:
+ return rotation;
+ case Rotation::CLOCK_WISE_90:
+ return Rotation::CLOCK_WISE_270;
+ case Rotation::CLOCK_WISE_180:
+ return Rotation::CLOCK_WISE_180;
+ case Rotation::CLOCK_WISE_270:
+ return Rotation::CLOCK_WISE_90;
+ }
+ RTC_DCHECK_NOTREACHED();
+ return Rotation::CLOCK_WISE_0;
+}
+
+DesktopSize RotateSize(DesktopSize size, Rotation rotation) {
+ switch (rotation) {
+ case Rotation::CLOCK_WISE_0:
+ case Rotation::CLOCK_WISE_180:
+ return size;
+ case Rotation::CLOCK_WISE_90:
+ case Rotation::CLOCK_WISE_270:
+ return DesktopSize(size.height(), size.width());
+ }
+ RTC_DCHECK_NOTREACHED();
+ return DesktopSize();
+}
+
+DesktopRect RotateRect(DesktopRect rect, DesktopSize size, Rotation rotation) {
+ switch (rotation) {
+ case Rotation::CLOCK_WISE_0:
+ return rect;
+ case Rotation::CLOCK_WISE_90:
+ return DesktopRect::MakeXYWH(size.height() - rect.bottom(), rect.left(),
+ rect.height(), rect.width());
+ case Rotation::CLOCK_WISE_180:
+ return DesktopRect::MakeXYWH(size.width() - rect.right(),
+ size.height() - rect.bottom(), rect.width(),
+ rect.height());
+ case Rotation::CLOCK_WISE_270:
+ return DesktopRect::MakeXYWH(rect.top(), size.width() - rect.right(),
+ rect.height(), rect.width());
+ }
+ RTC_DCHECK_NOTREACHED();
+ return DesktopRect();
+}
+
+void RotateDesktopFrame(const DesktopFrame& source,
+ const DesktopRect& source_rect,
+ const Rotation& rotation,
+ const DesktopVector& target_offset,
+ DesktopFrame* target) {
+ RTC_DCHECK(target);
+ RTC_DCHECK(DesktopRect::MakeSize(source.size()).ContainsRect(source_rect));
+ // The rectangle in `target`.
+ const DesktopRect target_rect =
+ RotateAndOffsetRect(source_rect, source.size(), rotation, target_offset);
+ RTC_DCHECK(DesktopRect::MakeSize(target->size()).ContainsRect(target_rect));
+
+ if (target_rect.is_empty()) {
+ return;
+ }
+
+ int result = libyuv::ARGBRotate(
+ source.GetFrameDataAtPos(source_rect.top_left()), source.stride(),
+ target->GetFrameDataAtPos(target_rect.top_left()), target->stride(),
+ source_rect.width(), source_rect.height(),
+ ToLibyuvRotationMode(rotation));
+ RTC_DCHECK_EQ(result, 0);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_frame_rotation.h b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_rotation.h
new file mode 100644
index 0000000000..6b51b2f883
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_rotation.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_ROTATION_H_
+#define MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_ROTATION_H_
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+// Represents the rotation of a DesktopFrame.
+enum class Rotation {
+ CLOCK_WISE_0,
+ CLOCK_WISE_90,
+ CLOCK_WISE_180,
+ CLOCK_WISE_270,
+};
+
+// Rotates input DesktopFrame `source`, copies pixel in an unrotated rectangle
+// `source_rect` into the target rectangle of another DesktopFrame `target`.
+// Target rectangle here is the rotated `source_rect` plus `target_offset`.
+// `rotation` specifies `source` to `target` rotation. `source_rect` is in
+// `source` coordinate. `target_offset` is in `target` coordinate.
+// This function triggers check failure if `source` does not cover the
+// `source_rect`, or `target` does not cover the rotated `rect`.
+void RotateDesktopFrame(const DesktopFrame& source,
+ const DesktopRect& source_rect,
+ const Rotation& rotation,
+ const DesktopVector& target_offset,
+ DesktopFrame* target);
+
+// Returns a reverse rotation of `rotation`.
+Rotation ReverseRotation(Rotation rotation);
+
+// Returns a rotated DesktopSize of `size`.
+DesktopSize RotateSize(DesktopSize size, Rotation rotation);
+
+// Returns a rotated DesktopRect of `rect`. The `size` represents the size of
+// the DesktopFrame which `rect` belongs in.
+DesktopRect RotateRect(DesktopRect rect, DesktopSize size, Rotation rotation);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_ROTATION_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_frame_rotation_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_rotation_unittest.cc
new file mode 100644
index 0000000000..782ca63e61
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_rotation_unittest.cc
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_frame_rotation.h"
+
+#include <stdint.h>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/test_utils.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+// A DesktopFrame implementation which stores data in an external int array.
+class ArrayDesktopFrame : public DesktopFrame {
+ public:
+ ArrayDesktopFrame(DesktopSize size, uint32_t* data);
+ ~ArrayDesktopFrame() override;
+};
+
+ArrayDesktopFrame::ArrayDesktopFrame(DesktopSize size, uint32_t* data)
+ : DesktopFrame(size,
+ size.width() * kBytesPerPixel,
+ reinterpret_cast<uint8_t*>(data),
+ nullptr) {}
+
+ArrayDesktopFrame::~ArrayDesktopFrame() = default;
+
+} // namespace
+
+TEST(DesktopFrameRotationTest, CopyRect3x4) {
+ // A DesktopFrame of 4-pixel width by 3-pixel height.
+ static uint32_t frame_pixels[] = {
+ 0, 1, 2, 3, //
+ 4, 5, 6, 7, //
+ 8, 9, 10, 11, //
+ };
+ ArrayDesktopFrame frame(DesktopSize(4, 3), frame_pixels);
+
+ {
+ BasicDesktopFrame target(DesktopSize(4, 3));
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_0, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(frame, target));
+ }
+
+ // After Rotating clock-wise 90 degree
+ {
+ static uint32_t expected_pixels[] = {
+ 8, 4, 0, //
+ 9, 5, 1, //
+ 10, 6, 2, //
+ 11, 7, 3, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(3, 4), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(3, 4));
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_90, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ }
+
+ // After Rotating clock-wise 180 degree
+ {
+ static uint32_t expected_pixels[] = {
+ 11, 10, 9, 8, //
+ 7, 6, 5, 4, //
+ 3, 2, 1, 0, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(4, 3), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(4, 3));
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_180, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ }
+
+ // After Rotating clock-wise 270 degree
+ {
+ static uint32_t expected_pixels[] = {
+ 3, 7, 11, //
+ 2, 6, 10, //
+ 1, 5, 9, //
+ 0, 4, 8, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(3, 4), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(3, 4));
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_270, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ }
+}
+
+TEST(DesktopFrameRotationTest, CopyRect3x5) {
+ // A DesktopFrame of 5-pixel width by 3-pixel height.
+ static uint32_t frame_pixels[] = {
+ 0, 1, 2, 3, 4, //
+ 5, 6, 7, 8, 9, //
+ 10, 11, 12, 13, 14, //
+ };
+ ArrayDesktopFrame frame(DesktopSize(5, 3), frame_pixels);
+
+ {
+ BasicDesktopFrame target(DesktopSize(5, 3));
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_0, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, frame));
+ }
+
+ // After Rotating clock-wise 90 degree
+ {
+ static uint32_t expected_pixels[] = {
+ 10, 5, 0, //
+ 11, 6, 1, //
+ 12, 7, 2, //
+ 13, 8, 3, //
+ 14, 9, 4, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(3, 5), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(3, 5));
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_90, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ }
+
+ // After Rotating clock-wise 180 degree
+ {
+ static uint32_t expected_pixels[]{
+ 14, 13, 12, 11, 10, //
+ 9, 8, 7, 6, 5, //
+ 4, 3, 2, 1, 0, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(5, 3), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(5, 3));
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_180, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ }
+
+ // After Rotating clock-wise 270 degree
+ {
+ static uint32_t expected_pixels[] = {
+ 4, 9, 14, //
+ 3, 8, 13, //
+ 2, 7, 12, //
+ 1, 6, 11, //
+ 0, 5, 10, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(3, 5), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(3, 5));
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_270, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ }
+}
+
+TEST(DesktopFrameRotationTest, PartialCopyRect3x5) {
+ // A DesktopFrame of 5-pixel width by 3-pixel height.
+ static uint32_t frame_pixels[] = {
+ 0, 1, 2, 3, 4, //
+ 5, 6, 7, 8, 9, //
+ 10, 11, 12, 13, 14, //
+ };
+ ArrayDesktopFrame frame(DesktopSize(5, 3), frame_pixels);
+
+ {
+ static uint32_t expected_pixels[] = {
+ 0, 0, 0, 0, 0, //
+ 0, 6, 7, 8, 0, //
+ 0, 0, 0, 0, 0, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(5, 3), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(5, 3));
+ ClearDesktopFrame(&target);
+ RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 1, 3, 1),
+ Rotation::CLOCK_WISE_0, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ }
+
+ {
+ static uint32_t expected_pixels[] = {
+ 0, 1, 2, 3, 0, //
+ 0, 6, 7, 8, 0, //
+ 0, 0, 0, 0, 0, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(5, 3), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(5, 3));
+ ClearDesktopFrame(&target);
+ RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 0, 3, 2),
+ Rotation::CLOCK_WISE_0, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ }
+
+ // After Rotating clock-wise 90 degree
+ {
+ static uint32_t expected_pixels[] = {
+ 0, 0, 0, //
+ 0, 6, 0, //
+ 0, 7, 0, //
+ 0, 8, 0, //
+ 0, 0, 0, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(3, 5), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(3, 5));
+ ClearDesktopFrame(&target);
+ RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 1, 3, 1),
+ Rotation::CLOCK_WISE_90, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ }
+
+ {
+ static uint32_t expected_pixels[] = {
+ 0, 0, 0, //
+ 11, 6, 0, //
+ 12, 7, 0, //
+ 13, 8, 0, //
+ 0, 0, 0, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(3, 5), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(3, 5));
+ ClearDesktopFrame(&target);
+ RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 1, 3, 2),
+ Rotation::CLOCK_WISE_90, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ }
+
+ // After Rotating clock-wise 180 degree
+ {
+ static uint32_t expected_pixels[] = {
+ 0, 0, 0, 0, 0, //
+ 0, 8, 7, 6, 0, //
+ 0, 0, 0, 0, 0, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(5, 3), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(5, 3));
+ ClearDesktopFrame(&target);
+ RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 1, 3, 1),
+ Rotation::CLOCK_WISE_180, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ }
+
+ {
+ static uint32_t expected_pixels[] = {
+ 0, 13, 12, 11, 0, //
+ 0, 8, 7, 6, 0, //
+ 0, 0, 0, 0, 0, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(5, 3), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(5, 3));
+ ClearDesktopFrame(&target);
+ RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 1, 3, 2),
+ Rotation::CLOCK_WISE_180, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ }
+
+ // After Rotating clock-wise 270 degree
+ {
+ static uint32_t expected_pixels[] = {
+ 0, 0, 0, //
+ 0, 8, 0, //
+ 0, 7, 0, //
+ 0, 6, 0, //
+ 0, 0, 0, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(3, 5), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(3, 5));
+ ClearDesktopFrame(&target);
+ RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 1, 3, 1),
+ Rotation::CLOCK_WISE_270, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ }
+
+ {
+ static uint32_t expected_pixels[] = {
+ 0, 0, 0, //
+ 3, 8, 0, //
+ 2, 7, 0, //
+ 1, 6, 0, //
+ 0, 0, 0, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(3, 5), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(3, 5));
+ ClearDesktopFrame(&target);
+ RotateDesktopFrame(frame, DesktopRect::MakeXYWH(1, 0, 3, 2),
+ Rotation::CLOCK_WISE_270, DesktopVector(), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ }
+}
+
+TEST(DesktopFrameRotationTest, WithOffset) {
+ // A DesktopFrame of 4-pixel width by 3-pixel height.
+ static uint32_t frame_pixels[] = {
+ 0, 1, 2, 3, //
+ 4, 5, 6, 7, //
+ 8, 9, 10, 11, //
+ };
+ ArrayDesktopFrame frame(DesktopSize(4, 3), frame_pixels);
+
+ {
+ static uint32_t expected_pixels[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 1, 2, 3, 0, 0, 0, //
+ 0, 4, 5, 6, 7, 0, 0, 0, //
+ 0, 8, 9, 10, 11, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(8, 6), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(8, 6));
+ ClearDesktopFrame(&target);
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_0, DesktopVector(1, 1), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ target.mutable_updated_region()->Subtract(
+ DesktopRect::MakeOriginSize(DesktopVector(1, 1), frame.size()));
+ ASSERT_TRUE(target.updated_region().is_empty());
+ }
+
+ {
+ static uint32_t expected_pixels[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 11, 10, 9, 8, 0, 0, 0, //
+ 0, 7, 6, 5, 4, 0, 0, 0, //
+ 0, 3, 2, 1, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, 0, 0, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(8, 6), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(8, 6));
+ ClearDesktopFrame(&target);
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_180, DesktopVector(1, 1), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ target.mutable_updated_region()->Subtract(
+ DesktopRect::MakeOriginSize(DesktopVector(1, 1), frame.size()));
+ ASSERT_TRUE(target.updated_region().is_empty());
+ }
+
+ {
+ static uint32_t expected_pixels[] = {
+ 0, 0, 0, 0, 0, 0, //
+ 0, 8, 4, 0, 0, 0, //
+ 0, 9, 5, 1, 0, 0, //
+ 0, 10, 6, 2, 0, 0, //
+ 0, 11, 7, 3, 0, 0, //
+ 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(6, 8), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(6, 8));
+ ClearDesktopFrame(&target);
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_90, DesktopVector(1, 1), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ target.mutable_updated_region()->Subtract(
+ DesktopRect::MakeXYWH(1, 1, 3, 4));
+ ASSERT_TRUE(target.updated_region().is_empty());
+ }
+
+ {
+ static uint32_t expected_pixels[] = {
+ 0, 0, 0, 0, 0, 0, //
+ 0, 3, 7, 11, 0, 0, //
+ 0, 2, 6, 10, 0, 0, //
+ 0, 1, 5, 9, 0, 0, //
+ 0, 0, 4, 8, 0, 0, //
+ 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, //
+ 0, 0, 0, 0, 0, 0, //
+ };
+ ArrayDesktopFrame expected(DesktopSize(6, 8), expected_pixels);
+
+ BasicDesktopFrame target(DesktopSize(6, 8));
+ ClearDesktopFrame(&target);
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_270, DesktopVector(1, 1), &target);
+ ASSERT_TRUE(DesktopFrameDataEquals(target, expected));
+ target.mutable_updated_region()->Subtract(
+ DesktopRect::MakeXYWH(1, 1, 3, 4));
+ ASSERT_TRUE(target.updated_region().is_empty());
+ }
+}
+
+// On a typical machine (Intel(R) Xeon(R) E5-1650 v3 @ 3.50GHz, with O2
+// optimization, the following case uses ~1.4s to finish. It means entirely
+// rotating one 2048 x 1536 frame, which is a large enough number to cover most
+// of desktop computer users, uses around 14ms.
+TEST(DesktopFrameRotationTest, DISABLED_PerformanceTest) {
+ BasicDesktopFrame frame(DesktopSize(2048, 1536));
+ BasicDesktopFrame target(DesktopSize(1536, 2048));
+ BasicDesktopFrame target2(DesktopSize(2048, 1536));
+ for (int i = 0; i < 100; i++) {
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_90, DesktopVector(), &target);
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_270, DesktopVector(), &target);
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_0, DesktopVector(), &target2);
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_180, DesktopVector(), &target2);
+ }
+}
+
+// On a typical machine (Intel(R) Xeon(R) E5-1650 v3 @ 3.50GHz, with O2
+// optimization, the following case uses ~6.7s to finish. It means entirely
+// rotating one 4096 x 3072 frame uses around 67ms.
+TEST(DesktopFrameRotationTest, DISABLED_PerformanceTestOnLargeScreen) {
+ BasicDesktopFrame frame(DesktopSize(4096, 3072));
+ BasicDesktopFrame target(DesktopSize(3072, 4096));
+ BasicDesktopFrame target2(DesktopSize(4096, 3072));
+ for (int i = 0; i < 100; i++) {
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_90, DesktopVector(), &target);
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_270, DesktopVector(), &target);
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_0, DesktopVector(), &target2);
+ RotateDesktopFrame(frame, DesktopRect::MakeSize(frame.size()),
+ Rotation::CLOCK_WISE_180, DesktopVector(), &target2);
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_frame_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_unittest.cc
new file mode 100644
index 0000000000..ce0cbb45f5
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_unittest.cc
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_frame.h"
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/test_utils.h"
+#include "rtc_base/arraysize.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+std::unique_ptr<DesktopFrame> CreateTestFrame(DesktopRect rect,
+ int pixels_value) {
+ DesktopSize size = rect.size();
+ auto frame = std::make_unique<BasicDesktopFrame>(size);
+ frame->set_top_left(rect.top_left());
+ memset(frame->data(), pixels_value, frame->stride() * size.height());
+ return frame;
+}
+
+struct TestData {
+ const char* description;
+ DesktopRect dest_frame_rect;
+ DesktopRect src_frame_rect;
+ double horizontal_scale;
+ double vertical_scale;
+ DesktopRect expected_overlap_rect;
+};
+
+void RunTest(const TestData& test) {
+ // Copy a source frame with all bits set into a dest frame with none set.
+ auto dest_frame = CreateTestFrame(test.dest_frame_rect, 0);
+ auto src_frame = CreateTestFrame(test.src_frame_rect, 0xff);
+
+ dest_frame->CopyIntersectingPixelsFrom(
+ *src_frame, test.horizontal_scale, test.vertical_scale);
+
+ // Translate the expected overlap rect to be relative to the dest frame/rect.
+ DesktopVector dest_frame_origin = test.dest_frame_rect.top_left();
+ DesktopRect relative_expected_overlap_rect = test.expected_overlap_rect;
+ relative_expected_overlap_rect.Translate(-dest_frame_origin.x(),
+ -dest_frame_origin.y());
+
+ // Confirm bits are now set in the dest frame if & only if they fall in the
+ // expected range.
+ for (int y = 0; y < dest_frame->size().height(); ++y) {
+ SCOPED_TRACE(y);
+
+ for (int x = 0; x < dest_frame->size().width(); ++x) {
+ SCOPED_TRACE(x);
+
+ DesktopVector point(x, y);
+ uint8_t* data = dest_frame->GetFrameDataAtPos(point);
+ uint32_t pixel_value = *reinterpret_cast<uint32_t*>(data);
+ bool was_copied = pixel_value == 0xffffffff;
+ ASSERT_TRUE(was_copied || pixel_value == 0);
+
+ bool expected_to_be_copied =
+ relative_expected_overlap_rect.Contains(point);
+
+ ASSERT_EQ(was_copied, expected_to_be_copied);
+ }
+ }
+}
+
+void RunTests(const TestData* tests, int num_tests) {
+ for (int i = 0; i < num_tests; i++) {
+ const TestData& test = tests[i];
+
+ SCOPED_TRACE(test.description);
+
+ RunTest(test);
+ }
+}
+
+} // namespace
+
+TEST(DesktopFrameTest, CopyIntersectingPixelsMatchingRects) {
+ const TestData tests[] = {
+ {"0 origin",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ 1.0, 1.0,
+ DesktopRect::MakeXYWH(0, 0, 2, 2)},
+
+ {"Negative origin",
+ DesktopRect::MakeXYWH(-1, -1, 2, 2),
+ DesktopRect::MakeXYWH(-1, -1, 2, 2),
+ 1.0, 1.0,
+ DesktopRect::MakeXYWH(-1, -1, 2, 2)}
+ };
+
+ RunTests(tests, arraysize(tests));
+}
+
+TEST(DesktopFrameTest, CopyIntersectingPixelsMatchingRectsScaled) {
+ // The scale factors shouldn't affect matching rects (they're only applied
+ // to any difference between the origins)
+ const TestData tests[] = {
+ {"0 origin 2x",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ 2.0, 2.0,
+ DesktopRect::MakeXYWH(0, 0, 2, 2)},
+
+ {"0 origin 0.5x",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ 0.5, 0.5,
+ DesktopRect::MakeXYWH(0, 0, 2, 2)},
+
+ {"Negative origin 2x",
+ DesktopRect::MakeXYWH(-1, -1, 2, 2),
+ DesktopRect::MakeXYWH(-1, -1, 2, 2),
+ 2.0, 2.0,
+ DesktopRect::MakeXYWH(-1, -1, 2, 2)},
+
+ {"Negative origin 0.5x",
+ DesktopRect::MakeXYWH(-1, -1, 2, 2),
+ DesktopRect::MakeXYWH(-1, -1, 2, 2),
+ 0.5, 0.5,
+ DesktopRect::MakeXYWH(-1, -1, 2, 2)}
+ };
+
+ RunTests(tests, arraysize(tests));
+}
+
+TEST(DesktopFrameTest, CopyIntersectingPixelsFullyContainedRects) {
+ const TestData tests[] = {
+ {"0 origin top left",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(0, 0, 1, 1),
+ 1.0, 1.0,
+ DesktopRect::MakeXYWH(0, 0, 1, 1)},
+
+ {"0 origin bottom right",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(1, 1, 1, 1),
+ 1.0, 1.0,
+ DesktopRect::MakeXYWH(1, 1, 1, 1)},
+
+ {"Negative origin bottom left",
+ DesktopRect::MakeXYWH(-1, -1, 2, 2),
+ DesktopRect::MakeXYWH(-1, 0, 1, 1),
+ 1.0, 1.0,
+ DesktopRect::MakeXYWH(-1, 0, 1, 1)}
+ };
+
+ RunTests(tests, arraysize(tests));
+}
+
+TEST(DesktopFrameTest, CopyIntersectingPixelsFullyContainedRectsScaled) {
+ const TestData tests[] = {
+ {"0 origin top left 2x",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(0, 0, 1, 1),
+ 2.0, 2.0,
+ DesktopRect::MakeXYWH(0, 0, 1, 1)},
+
+ {"0 origin top left 0.5x",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(0, 0, 1, 1),
+ 0.5, 0.5,
+ DesktopRect::MakeXYWH(0, 0, 1, 1)},
+
+ {"0 origin bottom left 2x",
+ DesktopRect::MakeXYWH(0, 0, 4, 4),
+ DesktopRect::MakeXYWH(1, 1, 2, 2),
+ 2.0, 2.0,
+ DesktopRect::MakeXYWH(2, 2, 2, 2)},
+
+ {"0 origin bottom middle 2x/1x",
+ DesktopRect::MakeXYWH(0, 0, 4, 3),
+ DesktopRect::MakeXYWH(1, 1, 2, 2),
+ 2.0, 1.0,
+ DesktopRect::MakeXYWH(2, 1, 2, 2)},
+
+ {"0 origin middle 0.5x",
+ DesktopRect::MakeXYWH(0, 0, 3, 3),
+ DesktopRect::MakeXYWH(2, 2, 1, 1),
+ 0.5, 0.5,
+ DesktopRect::MakeXYWH(1, 1, 1, 1)},
+
+ {"Negative origin bottom left 2x",
+ DesktopRect::MakeXYWH(-1, -1, 3, 3),
+ DesktopRect::MakeXYWH(-1, 0, 1, 1),
+ 2.0, 2.0,
+ DesktopRect::MakeXYWH(-1, 1, 1, 1)},
+
+ {"Negative origin near middle 0.5x",
+ DesktopRect::MakeXYWH(-2, -2, 2, 2),
+ DesktopRect::MakeXYWH(0, 0, 1, 1),
+ 0.5, 0.5,
+ DesktopRect::MakeXYWH(-1, -1, 1, 1)}
+ };
+
+ RunTests(tests, arraysize(tests));
+}
+
+
+TEST(DesktopFrameTest, CopyIntersectingPixelsPartiallyContainedRects) {
+ const TestData tests[] = {
+ {"Top left",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(-1, -1, 2, 2),
+ 1.0, 1.0,
+ DesktopRect::MakeXYWH(0, 0, 1, 1)},
+
+ {"Top right",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(1, -1, 2, 2),
+ 1.0, 1.0,
+ DesktopRect::MakeXYWH(1, 0, 1, 1)},
+
+ {"Bottom right",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(1, 1, 2, 2),
+ 1.0, 1.0,
+ DesktopRect::MakeXYWH(1, 1, 1, 1)},
+
+ {"Bottom left",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(-1, 1, 2, 2),
+ 1.0, 1.0,
+ DesktopRect::MakeXYWH(0, 1, 1, 1)}
+ };
+
+ RunTests(tests, arraysize(tests));
+}
+
+TEST(DesktopFrameTest, CopyIntersectingPixelsPartiallyContainedRectsScaled) {
+ const TestData tests[] = {
+ {"Top left 2x",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(-1, -1, 3, 3),
+ 2.0, 2.0,
+ DesktopRect::MakeXYWH(0, 0, 1, 1)},
+
+ {"Top right 0.5x",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(2, -2, 2, 2),
+ 0.5, 0.5,
+ DesktopRect::MakeXYWH(1, 0, 1, 1)},
+
+ {"Bottom right 2x",
+ DesktopRect::MakeXYWH(0, 0, 3, 3),
+ DesktopRect::MakeXYWH(-1, 1, 3, 3),
+ 2.0, 2.0,
+ DesktopRect::MakeXYWH(0, 2, 1, 1)},
+
+ {"Bottom left 0.5x",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(-2, 2, 2, 2),
+ 0.5, 0.5,
+ DesktopRect::MakeXYWH(0, 1, 1, 1)}
+ };
+
+ RunTests(tests, arraysize(tests));
+}
+
+
+TEST(DesktopFrameTest, CopyIntersectingPixelsUncontainedRects) {
+ const TestData tests[] = {
+ {"Left",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(-1, 0, 1, 2),
+ 1.0, 1.0,
+ DesktopRect::MakeXYWH(0, 0, 0, 0)},
+
+ {"Top",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(0, -1, 2, 1),
+ 1.0, 1.0,
+ DesktopRect::MakeXYWH(0, 0, 0, 0)},
+
+ {"Right",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(2, 0, 1, 2),
+ 1.0, 1.0,
+ DesktopRect::MakeXYWH(0, 0, 0, 0)},
+
+
+ {"Bottom",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(0, 2, 2, 1),
+ 1.0, 1.0,
+ DesktopRect::MakeXYWH(0, 0, 0, 0)}
+ };
+
+ RunTests(tests, arraysize(tests));
+}
+
+TEST(DesktopFrameTest, CopyIntersectingPixelsUncontainedRectsScaled) {
+ const TestData tests[] = {
+ {"Left 2x",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(-1, 0, 2, 2),
+ 2.0, 2.0,
+ DesktopRect::MakeXYWH(0, 0, 0, 0)},
+
+ {"Top 0.5x",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(0, -2, 2, 1),
+ 0.5, 0.5,
+ DesktopRect::MakeXYWH(0, 0, 0, 0)},
+
+ {"Right 2x",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(1, 0, 1, 2),
+ 2.0, 2.0,
+ DesktopRect::MakeXYWH(0, 0, 0, 0)},
+
+
+ {"Bottom 0.5x",
+ DesktopRect::MakeXYWH(0, 0, 2, 2),
+ DesktopRect::MakeXYWH(0, 4, 2, 1),
+ 0.5, 0.5,
+ DesktopRect::MakeXYWH(0, 0, 0, 0)}
+ };
+
+ RunTests(tests, arraysize(tests));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_frame_win.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_win.cc
new file mode 100644
index 0000000000..262ebbdec0
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_win.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_frame_win.h"
+
+#include <utility>
+
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+DesktopFrameWin::DesktopFrameWin(DesktopSize size,
+ int stride,
+ uint8_t* data,
+ std::unique_ptr<SharedMemory> shared_memory,
+ HBITMAP bitmap)
+ : DesktopFrame(size, stride, data, shared_memory.get()),
+ bitmap_(bitmap),
+ owned_shared_memory_(std::move(shared_memory)) {}
+
+DesktopFrameWin::~DesktopFrameWin() {
+ DeleteObject(bitmap_);
+}
+
+// static
+std::unique_ptr<DesktopFrameWin> DesktopFrameWin::Create(
+ DesktopSize size,
+ SharedMemoryFactory* shared_memory_factory,
+ HDC hdc) {
+ int bytes_per_row = size.width() * kBytesPerPixel;
+ int buffer_size = bytes_per_row * size.height();
+
+ // Describe a device independent bitmap (DIB) that is the size of the desktop.
+ BITMAPINFO bmi = {};
+ bmi.bmiHeader.biHeight = -size.height();
+ bmi.bmiHeader.biWidth = size.width();
+ bmi.bmiHeader.biPlanes = 1;
+ bmi.bmiHeader.biBitCount = DesktopFrameWin::kBytesPerPixel * 8;
+ bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
+ bmi.bmiHeader.biSizeImage = bytes_per_row * size.height();
+
+ std::unique_ptr<SharedMemory> shared_memory;
+ HANDLE section_handle = nullptr;
+ if (shared_memory_factory) {
+ shared_memory = shared_memory_factory->CreateSharedMemory(buffer_size);
+ if (!shared_memory) {
+ RTC_LOG(LS_WARNING) << "Failed to allocate shared memory";
+ return nullptr;
+ }
+ section_handle = shared_memory->handle();
+ }
+ void* data = nullptr;
+ HBITMAP bitmap =
+ CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, &data, section_handle, 0);
+ if (!bitmap) {
+ RTC_LOG(LS_WARNING) << "Failed to allocate new window frame "
+ << GetLastError();
+ return nullptr;
+ }
+
+ return std::unique_ptr<DesktopFrameWin>(
+ new DesktopFrameWin(size, bytes_per_row, reinterpret_cast<uint8_t*>(data),
+ std::move(shared_memory), bitmap));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_frame_win.h b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_win.h
new file mode 100644
index 0000000000..f8faad6777
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_frame_win.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_WIN_H_
+#define MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_WIN_H_
+
+#include <windows.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_frame.h"
+
+namespace webrtc {
+
+// DesktopFrame implementation used by screen and window captures on Windows.
+// Frame data is stored in a GDI bitmap.
+class DesktopFrameWin : public DesktopFrame {
+ public:
+ ~DesktopFrameWin() override;
+
+ DesktopFrameWin(const DesktopFrameWin&) = delete;
+ DesktopFrameWin& operator=(const DesktopFrameWin&) = delete;
+
+ static std::unique_ptr<DesktopFrameWin>
+ Create(DesktopSize size, SharedMemoryFactory* shared_memory_factory, HDC hdc);
+
+ HBITMAP bitmap() { return bitmap_; }
+
+ private:
+ DesktopFrameWin(DesktopSize size,
+ int stride,
+ uint8_t* data,
+ std::unique_ptr<SharedMemory> shared_memory,
+ HBITMAP bitmap);
+
+ HBITMAP bitmap_;
+ std::unique_ptr<SharedMemory> owned_shared_memory_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_FRAME_WIN_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_geometry.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_geometry.cc
new file mode 100644
index 0000000000..e0a5d7af83
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_geometry.cc
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_geometry.h"
+
+#include <algorithm>
+#include <cmath>
+
+namespace webrtc {
+
+bool DesktopRect::Contains(const DesktopVector& point) const {
+ return point.x() >= left() && point.x() < right() && point.y() >= top() &&
+ point.y() < bottom();
+}
+
+bool DesktopRect::ContainsRect(const DesktopRect& rect) const {
+ return rect.left() >= left() && rect.right() <= right() &&
+ rect.top() >= top() && rect.bottom() <= bottom();
+}
+
+void DesktopRect::IntersectWith(const DesktopRect& rect) {
+ left_ = std::max(left(), rect.left());
+ top_ = std::max(top(), rect.top());
+ right_ = std::min(right(), rect.right());
+ bottom_ = std::min(bottom(), rect.bottom());
+ if (is_empty()) {
+ left_ = 0;
+ top_ = 0;
+ right_ = 0;
+ bottom_ = 0;
+ }
+}
+
+void DesktopRect::UnionWith(const DesktopRect& rect) {
+ if (is_empty()) {
+ *this = rect;
+ return;
+ }
+
+ if (rect.is_empty()) {
+ return;
+ }
+
+ left_ = std::min(left(), rect.left());
+ top_ = std::min(top(), rect.top());
+ right_ = std::max(right(), rect.right());
+ bottom_ = std::max(bottom(), rect.bottom());
+}
+
+void DesktopRect::Translate(int32_t dx, int32_t dy) {
+ left_ += dx;
+ top_ += dy;
+ right_ += dx;
+ bottom_ += dy;
+}
+
+void DesktopRect::Extend(int32_t left_offset,
+ int32_t top_offset,
+ int32_t right_offset,
+ int32_t bottom_offset) {
+ left_ -= left_offset;
+ top_ -= top_offset;
+ right_ += right_offset;
+ bottom_ += bottom_offset;
+}
+
+void DesktopRect::Scale(double horizontal, double vertical) {
+ right_ += static_cast<int>(std::round(width() * (horizontal - 1)));
+ bottom_ += static_cast<int>(std::round(height() * (vertical - 1)));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_geometry.h b/third_party/libwebrtc/modules/desktop_capture/desktop_geometry.h
new file mode 100644
index 0000000000..691455df57
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_geometry.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_DESKTOP_GEOMETRY_H_
+#define MODULES_DESKTOP_CAPTURE_DESKTOP_GEOMETRY_H_
+
+#include <stdint.h>
+
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// A vector in the 2D integer space. E.g. can be used to represent screen DPI.
+class DesktopVector {
+ public:
+ DesktopVector() : x_(0), y_(0) {}
+ DesktopVector(int32_t x, int32_t y) : x_(x), y_(y) {}
+
+ int32_t x() const { return x_; }
+ int32_t y() const { return y_; }
+ bool is_zero() const { return x_ == 0 && y_ == 0; }
+
+ bool equals(const DesktopVector& other) const {
+ return x_ == other.x_ && y_ == other.y_;
+ }
+
+ void set(int32_t x, int32_t y) {
+ x_ = x;
+ y_ = y;
+ }
+
+ DesktopVector add(const DesktopVector& other) const {
+ return DesktopVector(x() + other.x(), y() + other.y());
+ }
+ DesktopVector subtract(const DesktopVector& other) const {
+ return DesktopVector(x() - other.x(), y() - other.y());
+ }
+
+ DesktopVector operator-() const { return DesktopVector(-x_, -y_); }
+
+ private:
+ int32_t x_;
+ int32_t y_;
+};
+
+// Type used to represent screen/window size.
+class DesktopSize {
+ public:
+ DesktopSize() : width_(0), height_(0) {}
+ DesktopSize(int32_t width, int32_t height) : width_(width), height_(height) {}
+
+ int32_t width() const { return width_; }
+ int32_t height() const { return height_; }
+
+ bool is_empty() const { return width_ <= 0 || height_ <= 0; }
+
+ bool equals(const DesktopSize& other) const {
+ return width_ == other.width_ && height_ == other.height_;
+ }
+
+ void set(int32_t width, int32_t height) {
+ width_ = width;
+ height_ = height;
+ }
+
+ private:
+ int32_t width_;
+ int32_t height_;
+};
+
+// Represents a rectangle on the screen.
+class RTC_EXPORT DesktopRect {
+ public:
+ static DesktopRect MakeSize(const DesktopSize& size) {
+ return DesktopRect(0, 0, size.width(), size.height());
+ }
+ static DesktopRect MakeWH(int32_t width, int32_t height) {
+ return DesktopRect(0, 0, width, height);
+ }
+ static DesktopRect MakeXYWH(int32_t x,
+ int32_t y,
+ int32_t width,
+ int32_t height) {
+ return DesktopRect(x, y, x + width, y + height);
+ }
+ static DesktopRect MakeLTRB(int32_t left,
+ int32_t top,
+ int32_t right,
+ int32_t bottom) {
+ return DesktopRect(left, top, right, bottom);
+ }
+ static DesktopRect MakeOriginSize(const DesktopVector& origin,
+ const DesktopSize& size) {
+ return MakeXYWH(origin.x(), origin.y(), size.width(), size.height());
+ }
+
+ DesktopRect() : left_(0), top_(0), right_(0), bottom_(0) {}
+
+ int32_t left() const { return left_; }
+ int32_t top() const { return top_; }
+ int32_t right() const { return right_; }
+ int32_t bottom() const { return bottom_; }
+ int32_t width() const { return right_ - left_; }
+ int32_t height() const { return bottom_ - top_; }
+
+ void set_width(int32_t width) { right_ = left_ + width; }
+ void set_height(int32_t height) { bottom_ = top_ + height; }
+
+ DesktopVector top_left() const { return DesktopVector(left_, top_); }
+ DesktopSize size() const { return DesktopSize(width(), height()); }
+
+ bool is_empty() const { return left_ >= right_ || top_ >= bottom_; }
+
+ bool equals(const DesktopRect& other) const {
+ return left_ == other.left_ && top_ == other.top_ &&
+ right_ == other.right_ && bottom_ == other.bottom_;
+ }
+
+ // Returns true if `point` lies within the rectangle boundaries.
+ bool Contains(const DesktopVector& point) const;
+
+ // Returns true if `rect` lies within the boundaries of this rectangle.
+ bool ContainsRect(const DesktopRect& rect) const;
+
+ // Finds intersection with `rect`.
+ void IntersectWith(const DesktopRect& rect);
+
+ // Extends the rectangle to cover `rect`. If `this` is empty, replaces `this`
+ // with `rect`; if `rect` is empty, this function takes no effect.
+ void UnionWith(const DesktopRect& rect);
+
+ // Adds (dx, dy) to the position of the rectangle.
+ void Translate(int32_t dx, int32_t dy);
+ void Translate(DesktopVector d) { Translate(d.x(), d.y()); }
+
+ // Enlarges current DesktopRect by subtracting `left_offset` and `top_offset`
+ // from `left_` and `top_`, and adding `right_offset` and `bottom_offset` to
+ // `right_` and `bottom_`. This function does not normalize the result, so
+ // `left_` and `top_` may be less than zero or larger than `right_` and
+ // `bottom_`.
+ void Extend(int32_t left_offset,
+ int32_t top_offset,
+ int32_t right_offset,
+ int32_t bottom_offset);
+
+ // Scales current DesktopRect. This function does not impact the `top_` and
+ // `left_`.
+ void Scale(double horizontal, double vertical);
+
+ private:
+ DesktopRect(int32_t left, int32_t top, int32_t right, int32_t bottom)
+ : left_(left), top_(top), right_(right), bottom_(bottom) {}
+
+ int32_t left_;
+ int32_t top_;
+ int32_t right_;
+ int32_t bottom_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_GEOMETRY_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_geometry_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_geometry_unittest.cc
new file mode 100644
index 0000000000..f4a07fa46b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_geometry_unittest.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_geometry.h"
+
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(DesktopRectTest, UnionBetweenTwoNonEmptyRects) {
+ DesktopRect rect = DesktopRect::MakeLTRB(1, 1, 2, 2);
+ rect.UnionWith(DesktopRect::MakeLTRB(-2, -2, -1, -1));
+ ASSERT_TRUE(rect.equals(DesktopRect::MakeLTRB(-2, -2, 2, 2)));
+}
+
+TEST(DesktopRectTest, UnionWithEmptyRect) {
+ DesktopRect rect = DesktopRect::MakeWH(1, 1);
+ rect.UnionWith(DesktopRect());
+ ASSERT_TRUE(rect.equals(DesktopRect::MakeWH(1, 1)));
+
+ rect = DesktopRect::MakeXYWH(1, 1, 2, 2);
+ rect.UnionWith(DesktopRect());
+ ASSERT_TRUE(rect.equals(DesktopRect::MakeXYWH(1, 1, 2, 2)));
+
+ rect = DesktopRect::MakeXYWH(1, 1, 2, 2);
+ rect.UnionWith(DesktopRect::MakeXYWH(3, 3, 0, 0));
+ ASSERT_TRUE(rect.equals(DesktopRect::MakeXYWH(1, 1, 2, 2)));
+}
+
+TEST(DesktopRectTest, EmptyRectUnionWithNonEmptyOne) {
+ DesktopRect rect;
+ rect.UnionWith(DesktopRect::MakeWH(1, 1));
+ ASSERT_TRUE(rect.equals(DesktopRect::MakeWH(1, 1)));
+
+ rect = DesktopRect();
+ rect.UnionWith(DesktopRect::MakeXYWH(1, 1, 2, 2));
+ ASSERT_TRUE(rect.equals(DesktopRect::MakeXYWH(1, 1, 2, 2)));
+
+ rect = DesktopRect::MakeXYWH(3, 3, 0, 0);
+ rect.UnionWith(DesktopRect::MakeXYWH(1, 1, 2, 2));
+ ASSERT_TRUE(rect.equals(DesktopRect::MakeXYWH(1, 1, 2, 2)));
+}
+
+TEST(DesktopRectTest, EmptyRectUnionWithEmptyOne) {
+ DesktopRect rect;
+ rect.UnionWith(DesktopRect());
+ ASSERT_TRUE(rect.is_empty());
+
+ rect = DesktopRect::MakeXYWH(1, 1, 0, 0);
+ rect.UnionWith(DesktopRect());
+ ASSERT_TRUE(rect.is_empty());
+
+ rect = DesktopRect();
+ rect.UnionWith(DesktopRect::MakeXYWH(1, 1, 0, 0));
+ ASSERT_TRUE(rect.is_empty());
+
+ rect = DesktopRect::MakeXYWH(1, 1, 0, 0);
+ rect.UnionWith(DesktopRect::MakeXYWH(-1, -1, 0, 0));
+ ASSERT_TRUE(rect.is_empty());
+}
+
+TEST(DesktopRectTest, Scale) {
+ DesktopRect rect = DesktopRect::MakeXYWH(100, 100, 100, 100);
+ rect.Scale(1.1, 1.1);
+ ASSERT_EQ(rect.top(), 100);
+ ASSERT_EQ(rect.left(), 100);
+ ASSERT_EQ(rect.width(), 110);
+ ASSERT_EQ(rect.height(), 110);
+
+ rect = DesktopRect::MakeXYWH(100, 100, 100, 100);
+ rect.Scale(0.01, 0.01);
+ ASSERT_EQ(rect.top(), 100);
+ ASSERT_EQ(rect.left(), 100);
+ ASSERT_EQ(rect.width(), 1);
+ ASSERT_EQ(rect.height(), 1);
+
+ rect = DesktopRect::MakeXYWH(100, 100, 100, 100);
+ rect.Scale(1.1, 0.9);
+ ASSERT_EQ(rect.top(), 100);
+ ASSERT_EQ(rect.left(), 100);
+ ASSERT_EQ(rect.width(), 110);
+ ASSERT_EQ(rect.height(), 90);
+
+ rect = DesktopRect::MakeXYWH(0, 0, 100, 100);
+ rect.Scale(1.1, 1.1);
+ ASSERT_EQ(rect.top(), 0);
+ ASSERT_EQ(rect.left(), 0);
+ ASSERT_EQ(rect.width(), 110);
+ ASSERT_EQ(rect.height(), 110);
+
+ rect = DesktopRect::MakeXYWH(0, 100, 100, 100);
+ rect.Scale(1.1, 1.1);
+ ASSERT_EQ(rect.top(), 100);
+ ASSERT_EQ(rect.left(), 0);
+ ASSERT_EQ(rect.width(), 110);
+ ASSERT_EQ(rect.height(), 110);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_region.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_region.cc
new file mode 100644
index 0000000000..2c87c11eb3
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_region.cc
@@ -0,0 +1,567 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_region.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+DesktopRegion::RowSpan::RowSpan(int32_t left, int32_t right)
+ : left(left), right(right) {}
+
+DesktopRegion::Row::Row(const Row&) = default;
+DesktopRegion::Row::Row(Row&&) = default;
+
+DesktopRegion::Row::Row(int32_t top, int32_t bottom)
+ : top(top), bottom(bottom) {}
+
+DesktopRegion::Row::~Row() {}
+
+DesktopRegion::DesktopRegion() {}
+
+DesktopRegion::DesktopRegion(const DesktopRect& rect) {
+ AddRect(rect);
+}
+
+DesktopRegion::DesktopRegion(const DesktopRect* rects, int count) {
+ AddRects(rects, count);
+}
+
+DesktopRegion::DesktopRegion(const DesktopRegion& other) {
+ *this = other;
+}
+
+DesktopRegion::~DesktopRegion() {
+ Clear();
+}
+
+DesktopRegion& DesktopRegion::operator=(const DesktopRegion& other) {
+ Clear();
+ rows_ = other.rows_;
+ for (Rows::iterator it = rows_.begin(); it != rows_.end(); ++it) {
+ // Copy each row.
+ Row* row = it->second;
+ it->second = new Row(*row);
+ }
+ return *this;
+}
+
+bool DesktopRegion::Equals(const DesktopRegion& region) const {
+ // Iterate over rows of the tow regions and compare each row.
+ Rows::const_iterator it1 = rows_.begin();
+ Rows::const_iterator it2 = region.rows_.begin();
+ while (it1 != rows_.end()) {
+ if (it2 == region.rows_.end() || it1->first != it2->first ||
+ it1->second->top != it2->second->top ||
+ it1->second->bottom != it2->second->bottom ||
+ it1->second->spans != it2->second->spans) {
+ return false;
+ }
+ ++it1;
+ ++it2;
+ }
+ return it2 == region.rows_.end();
+}
+
+void DesktopRegion::Clear() {
+ for (Rows::iterator row = rows_.begin(); row != rows_.end(); ++row) {
+ delete row->second;
+ }
+ rows_.clear();
+}
+
+void DesktopRegion::SetRect(const DesktopRect& rect) {
+ Clear();
+ AddRect(rect);
+}
+
+void DesktopRegion::AddRect(const DesktopRect& rect) {
+ if (rect.is_empty())
+ return;
+
+ // Top of the part of the `rect` that hasn't been inserted yet. Increased as
+ // we iterate over the rows until it reaches `rect.bottom()`.
+ int top = rect.top();
+
+ // Iterate over all rows that may intersect with `rect` and add new rows when
+ // necessary.
+ Rows::iterator row = rows_.upper_bound(top);
+ while (top < rect.bottom()) {
+ if (row == rows_.end() || top < row->second->top) {
+ // If `top` is above the top of the current `row` then add a new row above
+ // the current one.
+ int32_t bottom = rect.bottom();
+ if (row != rows_.end() && row->second->top < bottom)
+ bottom = row->second->top;
+ row = rows_.insert(row, Rows::value_type(bottom, new Row(top, bottom)));
+ } else if (top > row->second->top) {
+ // If the `top` falls in the middle of the `row` then split `row` into
+ // two, at `top`, and leave `row` referring to the lower of the two,
+ // ready to insert a new span into.
+ RTC_DCHECK_LE(top, row->second->bottom);
+ Rows::iterator new_row = rows_.insert(
+ row, Rows::value_type(top, new Row(row->second->top, top)));
+ row->second->top = top;
+ new_row->second->spans = row->second->spans;
+ }
+
+ if (rect.bottom() < row->second->bottom) {
+ // If the bottom of the `rect` falls in the middle of the `row` split
+ // `row` into two, at `top`, and leave `row` referring to the upper of
+ // the two, ready to insert a new span into.
+ Rows::iterator new_row = rows_.insert(
+ row, Rows::value_type(rect.bottom(), new Row(top, rect.bottom())));
+ row->second->top = rect.bottom();
+ new_row->second->spans = row->second->spans;
+ row = new_row;
+ }
+
+ // Add a new span to the current row.
+ AddSpanToRow(row->second, rect.left(), rect.right());
+ top = row->second->bottom;
+
+ MergeWithPrecedingRow(row);
+
+ // Move to the next row.
+ ++row;
+ }
+
+ if (row != rows_.end())
+ MergeWithPrecedingRow(row);
+}
+
+void DesktopRegion::AddRects(const DesktopRect* rects, int count) {
+ for (int i = 0; i < count; ++i) {
+ AddRect(rects[i]);
+ }
+}
+
+void DesktopRegion::MergeWithPrecedingRow(Rows::iterator row) {
+ RTC_DCHECK(row != rows_.end());
+
+ if (row != rows_.begin()) {
+ Rows::iterator previous_row = row;
+ previous_row--;
+
+ // If `row` and `previous_row` are next to each other and contain the same
+ // set of spans then they can be merged.
+ if (previous_row->second->bottom == row->second->top &&
+ previous_row->second->spans == row->second->spans) {
+ row->second->top = previous_row->second->top;
+ delete previous_row->second;
+ rows_.erase(previous_row);
+ }
+ }
+}
+
+void DesktopRegion::AddRegion(const DesktopRegion& region) {
+ // TODO(sergeyu): This function is not optimized - potentially it can iterate
+ // over rows of the two regions similar to how it works in Intersect().
+ for (Iterator it(region); !it.IsAtEnd(); it.Advance()) {
+ AddRect(it.rect());
+ }
+}
+
+void DesktopRegion::Intersect(const DesktopRegion& region1,
+ const DesktopRegion& region2) {
+ Clear();
+
+ Rows::const_iterator it1 = region1.rows_.begin();
+ Rows::const_iterator end1 = region1.rows_.end();
+ Rows::const_iterator it2 = region2.rows_.begin();
+ Rows::const_iterator end2 = region2.rows_.end();
+ if (it1 == end1 || it2 == end2)
+ return;
+
+ while (it1 != end1 && it2 != end2) {
+ // Arrange for `it1` to always be the top-most of the rows.
+ if (it2->second->top < it1->second->top) {
+ std::swap(it1, it2);
+ std::swap(end1, end2);
+ }
+
+ // Skip `it1` if it doesn't intersect `it2` at all.
+ if (it1->second->bottom <= it2->second->top) {
+ ++it1;
+ continue;
+ }
+
+ // Top of the `it1` row is above the top of `it2`, so top of the
+ // intersection is always the top of `it2`.
+ int32_t top = it2->second->top;
+ int32_t bottom = std::min(it1->second->bottom, it2->second->bottom);
+
+ Rows::iterator new_row = rows_.insert(
+ rows_.end(), Rows::value_type(bottom, new Row(top, bottom)));
+ IntersectRows(it1->second->spans, it2->second->spans,
+ &new_row->second->spans);
+ if (new_row->second->spans.empty()) {
+ delete new_row->second;
+ rows_.erase(new_row);
+ } else {
+ MergeWithPrecedingRow(new_row);
+ }
+
+ // If `it1` was completely consumed, move to the next one.
+ if (it1->second->bottom == bottom)
+ ++it1;
+ // If `it2` was completely consumed, move to the next one.
+ if (it2->second->bottom == bottom)
+ ++it2;
+ }
+}
+
+// static
+void DesktopRegion::IntersectRows(const RowSpanSet& set1,
+ const RowSpanSet& set2,
+ RowSpanSet* output) {
+ RowSpanSet::const_iterator it1 = set1.begin();
+ RowSpanSet::const_iterator end1 = set1.end();
+ RowSpanSet::const_iterator it2 = set2.begin();
+ RowSpanSet::const_iterator end2 = set2.end();
+ RTC_DCHECK(it1 != end1 && it2 != end2);
+
+ do {
+ // Arrange for `it1` to always be the left-most of the spans.
+ if (it2->left < it1->left) {
+ std::swap(it1, it2);
+ std::swap(end1, end2);
+ }
+
+ // Skip `it1` if it doesn't intersect `it2` at all.
+ if (it1->right <= it2->left) {
+ ++it1;
+ continue;
+ }
+
+ int32_t left = it2->left;
+ int32_t right = std::min(it1->right, it2->right);
+ RTC_DCHECK_LT(left, right);
+
+ output->push_back(RowSpan(left, right));
+
+ // If `it1` was completely consumed, move to the next one.
+ if (it1->right == right)
+ ++it1;
+ // If `it2` was completely consumed, move to the next one.
+ if (it2->right == right)
+ ++it2;
+ } while (it1 != end1 && it2 != end2);
+}
+
+void DesktopRegion::IntersectWith(const DesktopRegion& region) {
+ DesktopRegion old_region;
+ Swap(&old_region);
+ Intersect(old_region, region);
+}
+
+void DesktopRegion::IntersectWith(const DesktopRect& rect) {
+ DesktopRegion region;
+ region.AddRect(rect);
+ IntersectWith(region);
+}
+
+void DesktopRegion::Subtract(const DesktopRegion& region) {
+ if (region.rows_.empty())
+ return;
+
+ // `row_b` refers to the current row being subtracted.
+ Rows::const_iterator row_b = region.rows_.begin();
+
+ // Current vertical position at which subtraction is happening.
+ int top = row_b->second->top;
+
+ // `row_a` refers to the current row we are subtracting from. Skip all rows
+ // above `top`.
+ Rows::iterator row_a = rows_.upper_bound(top);
+
+ // Step through rows of the both regions subtracting content of `row_b` from
+ // `row_a`.
+ while (row_a != rows_.end() && row_b != region.rows_.end()) {
+ // Skip `row_a` if it doesn't intersect with the `row_b`.
+ if (row_a->second->bottom <= top) {
+ // Each output row is merged with previously-processed rows before further
+ // rows are processed.
+ MergeWithPrecedingRow(row_a);
+ ++row_a;
+ continue;
+ }
+
+ if (top > row_a->second->top) {
+ // If `top` falls in the middle of `row_a` then split `row_a` into two, at
+ // `top`, and leave `row_a` referring to the lower of the two, ready to
+ // subtract spans from.
+ RTC_DCHECK_LE(top, row_a->second->bottom);
+ Rows::iterator new_row = rows_.insert(
+ row_a, Rows::value_type(top, new Row(row_a->second->top, top)));
+ row_a->second->top = top;
+ new_row->second->spans = row_a->second->spans;
+ } else if (top < row_a->second->top) {
+ // If the `top` is above `row_a` then skip the range between `top` and
+ // top of `row_a` because it's empty.
+ top = row_a->second->top;
+ if (top >= row_b->second->bottom) {
+ ++row_b;
+ if (row_b != region.rows_.end())
+ top = row_b->second->top;
+ continue;
+ }
+ }
+
+ if (row_b->second->bottom < row_a->second->bottom) {
+ // If the bottom of `row_b` falls in the middle of the `row_a` split
+ // `row_a` into two, at `top`, and leave `row_a` referring to the upper of
+ // the two, ready to subtract spans from.
+ int bottom = row_b->second->bottom;
+ Rows::iterator new_row =
+ rows_.insert(row_a, Rows::value_type(bottom, new Row(top, bottom)));
+ row_a->second->top = bottom;
+ new_row->second->spans = row_a->second->spans;
+ row_a = new_row;
+ }
+
+ // At this point the vertical range covered by `row_a` lays within the
+ // range covered by `row_b`. Subtract `row_b` spans from `row_a`.
+ RowSpanSet new_spans;
+ SubtractRows(row_a->second->spans, row_b->second->spans, &new_spans);
+ new_spans.swap(row_a->second->spans);
+ top = row_a->second->bottom;
+
+ if (top >= row_b->second->bottom) {
+ ++row_b;
+ if (row_b != region.rows_.end())
+ top = row_b->second->top;
+ }
+
+ // Check if the row is empty after subtraction and delete it. Otherwise move
+ // to the next one.
+ if (row_a->second->spans.empty()) {
+ Rows::iterator row_to_delete = row_a;
+ ++row_a;
+ delete row_to_delete->second;
+ rows_.erase(row_to_delete);
+ } else {
+ MergeWithPrecedingRow(row_a);
+ ++row_a;
+ }
+ }
+
+ if (row_a != rows_.end())
+ MergeWithPrecedingRow(row_a);
+}
+
+void DesktopRegion::Subtract(const DesktopRect& rect) {
+ DesktopRegion region;
+ region.AddRect(rect);
+ Subtract(region);
+}
+
+void DesktopRegion::Translate(int32_t dx, int32_t dy) {
+ Rows new_rows;
+
+ for (Rows::iterator it = rows_.begin(); it != rows_.end(); ++it) {
+ Row* row = it->second;
+
+ row->top += dy;
+ row->bottom += dy;
+
+ if (dx != 0) {
+ // Translate each span.
+ for (RowSpanSet::iterator span = row->spans.begin();
+ span != row->spans.end(); ++span) {
+ span->left += dx;
+ span->right += dx;
+ }
+ }
+
+ if (dy != 0)
+ new_rows.insert(new_rows.end(), Rows::value_type(row->bottom, row));
+ }
+
+ if (dy != 0)
+ new_rows.swap(rows_);
+}
+
+void DesktopRegion::Swap(DesktopRegion* region) {
+ rows_.swap(region->rows_);
+}
+
+// static
+bool DesktopRegion::CompareSpanRight(const RowSpan& r, int32_t value) {
+ return r.right < value;
+}
+
+// static
+bool DesktopRegion::CompareSpanLeft(const RowSpan& r, int32_t value) {
+ return r.left < value;
+}
+
+// static
+void DesktopRegion::AddSpanToRow(Row* row, int left, int right) {
+ // First check if the new span is located to the right of all existing spans.
+ // This is an optimization to avoid binary search in the case when rectangles
+ // are inserted sequentially from left to right.
+ if (row->spans.empty() || left > row->spans.back().right) {
+ row->spans.push_back(RowSpan(left, right));
+ return;
+ }
+
+ // Find the first span that ends at or after `left`.
+ RowSpanSet::iterator start = std::lower_bound(
+ row->spans.begin(), row->spans.end(), left, CompareSpanRight);
+ RTC_DCHECK(start < row->spans.end());
+
+ // Find the first span that starts after `right`.
+ RowSpanSet::iterator end =
+ std::lower_bound(start, row->spans.end(), right + 1, CompareSpanLeft);
+ if (end == row->spans.begin()) {
+ // There are no overlaps. Just insert the new span at the beginning.
+ row->spans.insert(row->spans.begin(), RowSpan(left, right));
+ return;
+ }
+
+ // Move end to the left, so that it points the last span that ends at or
+ // before `right`.
+ end--;
+
+ // At this point [start, end] is the range of spans that intersect with the
+ // new one.
+ if (end < start) {
+ // There are no overlaps. Just insert the new span at the correct position.
+ row->spans.insert(start, RowSpan(left, right));
+ return;
+ }
+
+ left = std::min(left, start->left);
+ right = std::max(right, end->right);
+
+ // Replace range [start, end] with the new span.
+ *start = RowSpan(left, right);
+ ++start;
+ ++end;
+ if (start < end)
+ row->spans.erase(start, end);
+}
+
+// static
+bool DesktopRegion::IsSpanInRow(const Row& row, const RowSpan& span) {
+ // Find the first span that starts at or after `span.left` and then check if
+ // it's the same span.
+ RowSpanSet::const_iterator it = std::lower_bound(
+ row.spans.begin(), row.spans.end(), span.left, CompareSpanLeft);
+ return it != row.spans.end() && *it == span;
+}
+
+// static
+void DesktopRegion::SubtractRows(const RowSpanSet& set_a,
+ const RowSpanSet& set_b,
+ RowSpanSet* output) {
+ RTC_DCHECK(!set_a.empty() && !set_b.empty());
+
+ RowSpanSet::const_iterator it_b = set_b.begin();
+
+ // Iterate over all spans in `set_a` adding parts of it that do not intersect
+ // with `set_b` to the `output`.
+ for (RowSpanSet::const_iterator it_a = set_a.begin(); it_a != set_a.end();
+ ++it_a) {
+ // If there is no intersection then append the current span and continue.
+ if (it_b == set_b.end() || it_a->right < it_b->left) {
+ output->push_back(*it_a);
+ continue;
+ }
+
+ // Iterate over `set_b` spans that may intersect with `it_a`.
+ int pos = it_a->left;
+ while (it_b != set_b.end() && it_b->left < it_a->right) {
+ if (it_b->left > pos)
+ output->push_back(RowSpan(pos, it_b->left));
+ if (it_b->right > pos) {
+ pos = it_b->right;
+ if (pos >= it_a->right)
+ break;
+ }
+ ++it_b;
+ }
+ if (pos < it_a->right)
+ output->push_back(RowSpan(pos, it_a->right));
+ }
+}
+
+DesktopRegion::Iterator::Iterator(const DesktopRegion& region)
+ : region_(region),
+ row_(region.rows_.begin()),
+ previous_row_(region.rows_.end()) {
+ if (!IsAtEnd()) {
+ RTC_DCHECK_GT(row_->second->spans.size(), 0);
+ row_span_ = row_->second->spans.begin();
+ UpdateCurrentRect();
+ }
+}
+
+DesktopRegion::Iterator::~Iterator() {}
+
+bool DesktopRegion::Iterator::IsAtEnd() const {
+ return row_ == region_.rows_.end();
+}
+
+void DesktopRegion::Iterator::Advance() {
+ RTC_DCHECK(!IsAtEnd());
+
+ while (true) {
+ ++row_span_;
+ if (row_span_ == row_->second->spans.end()) {
+ previous_row_ = row_;
+ ++row_;
+ if (row_ != region_.rows_.end()) {
+ RTC_DCHECK_GT(row_->second->spans.size(), 0);
+ row_span_ = row_->second->spans.begin();
+ }
+ }
+
+ if (IsAtEnd())
+ return;
+
+ // If the same span exists on the previous row then skip it, as we've
+ // already returned this span merged into the previous one, via
+ // UpdateCurrentRect().
+ if (previous_row_ != region_.rows_.end() &&
+ previous_row_->second->bottom == row_->second->top &&
+ IsSpanInRow(*previous_row_->second, *row_span_)) {
+ continue;
+ }
+
+ break;
+ }
+
+ RTC_DCHECK(!IsAtEnd());
+ UpdateCurrentRect();
+}
+
+void DesktopRegion::Iterator::UpdateCurrentRect() {
+ // Merge the current rectangle with the matching spans from later rows.
+ int bottom;
+ Rows::const_iterator bottom_row = row_;
+ Rows::const_iterator previous;
+ do {
+ bottom = bottom_row->second->bottom;
+ previous = bottom_row;
+ ++bottom_row;
+ } while (bottom_row != region_.rows_.end() &&
+ previous->second->bottom == bottom_row->second->top &&
+ IsSpanInRow(*bottom_row->second, *row_span_));
+ rect_ = DesktopRect::MakeLTRB(row_span_->left, row_->second->top,
+ row_span_->right, bottom);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_region.h b/third_party/libwebrtc/modules/desktop_capture/desktop_region.h
new file mode 100644
index 0000000000..ae9d8a0ba9
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_region.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_DESKTOP_REGION_H_
+#define MODULES_DESKTOP_CAPTURE_DESKTOP_REGION_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// DesktopRegion represents a region of the screen or window.
+//
+// Internally each region is stored as a set of rows where each row contains one
+// or more rectangles aligned vertically.
+class RTC_EXPORT DesktopRegion {
+ private:
+ // The following private types need to be declared first because they are used
+ // in the public Iterator.
+
+ // RowSpan represents a horizontal span withing a single row.
+ struct RowSpan {
+ RowSpan(int32_t left, int32_t right);
+
+ // Used by std::vector<>.
+ bool operator==(const RowSpan& that) const {
+ return left == that.left && right == that.right;
+ }
+
+ int32_t left;
+ int32_t right;
+ };
+
+ typedef std::vector<RowSpan> RowSpanSet;
+
+ // Row represents a single row of a region. A row is set of rectangles that
+ // have the same vertical position.
+ struct Row {
+ Row(const Row&);
+ Row(Row&&);
+ Row(int32_t top, int32_t bottom);
+ ~Row();
+
+ int32_t top;
+ int32_t bottom;
+
+ RowSpanSet spans;
+ };
+
+ // Type used to store list of rows in the region. The bottom position of row
+ // is used as the key so that rows are always ordered by their position. The
+ // map stores pointers to make Translate() more efficient.
+ typedef std::map<int, Row*> Rows;
+
+ public:
+ // Iterator that can be used to iterate over rectangles of a DesktopRegion.
+ // The region must not be mutated while the iterator is used.
+ class RTC_EXPORT Iterator {
+ public:
+ explicit Iterator(const DesktopRegion& target);
+ ~Iterator();
+
+ bool IsAtEnd() const;
+ void Advance();
+
+ const DesktopRect& rect() const { return rect_; }
+
+ private:
+ const DesktopRegion& region_;
+
+ // Updates `rect_` based on the current `row_` and `row_span_`. If
+ // `row_span_` matches spans on consecutive rows then they are also merged
+ // into `rect_`, to generate more efficient output.
+ void UpdateCurrentRect();
+
+ Rows::const_iterator row_;
+ Rows::const_iterator previous_row_;
+ RowSpanSet::const_iterator row_span_;
+ DesktopRect rect_;
+ };
+
+ DesktopRegion();
+ explicit DesktopRegion(const DesktopRect& rect);
+ DesktopRegion(const DesktopRect* rects, int count);
+ DesktopRegion(const DesktopRegion& other);
+ ~DesktopRegion();
+
+ DesktopRegion& operator=(const DesktopRegion& other);
+
+ bool is_empty() const { return rows_.empty(); }
+
+ bool Equals(const DesktopRegion& region) const;
+
+ // Reset the region to be empty.
+ void Clear();
+
+ // Reset region to contain just `rect`.
+ void SetRect(const DesktopRect& rect);
+
+ // Adds specified rect(s) or region to the region.
+ void AddRect(const DesktopRect& rect);
+ void AddRects(const DesktopRect* rects, int count);
+ void AddRegion(const DesktopRegion& region);
+
+ // Finds intersection of two regions and stores them in the current region.
+ void Intersect(const DesktopRegion& region1, const DesktopRegion& region2);
+
+ // Same as above but intersects content of the current region with `region`.
+ void IntersectWith(const DesktopRegion& region);
+
+ // Clips the region by the `rect`.
+ void IntersectWith(const DesktopRect& rect);
+
+ // Subtracts `region` from the current content of the region.
+ void Subtract(const DesktopRegion& region);
+
+ // Subtracts `rect` from the current content of the region.
+ void Subtract(const DesktopRect& rect);
+
+ // Adds (dx, dy) to the position of the region.
+ void Translate(int32_t dx, int32_t dy);
+
+ void Swap(DesktopRegion* region);
+
+ private:
+ // Comparison functions used for std::lower_bound(). Compare left or right
+ // edges withs a given `value`.
+ static bool CompareSpanLeft(const RowSpan& r, int32_t value);
+ static bool CompareSpanRight(const RowSpan& r, int32_t value);
+
+ // Adds a new span to the row, coalescing spans if necessary.
+ static void AddSpanToRow(Row* row, int32_t left, int32_t right);
+
+ // Returns true if the `span` exists in the given `row`.
+ static bool IsSpanInRow(const Row& row, const RowSpan& rect);
+
+ // Calculates the intersection of two sets of spans.
+ static void IntersectRows(const RowSpanSet& set1,
+ const RowSpanSet& set2,
+ RowSpanSet* output);
+
+ static void SubtractRows(const RowSpanSet& set_a,
+ const RowSpanSet& set_b,
+ RowSpanSet* output);
+
+ // Merges `row` with the row above it if they contain the same spans. Doesn't
+ // do anything if called with `row` set to rows_.begin() (i.e. first row of
+ // the region). If the rows were merged `row` remains a valid iterator to the
+ // merged row.
+ void MergeWithPrecedingRow(Rows::iterator row);
+
+ Rows rows_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_REGION_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/desktop_region_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/desktop_region_unittest.cc
new file mode 100644
index 0000000000..b8bd78e990
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/desktop_region_unittest.cc
@@ -0,0 +1,834 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_region.h"
+
+#include <stdlib.h>
+
+#include <algorithm>
+#include <cstdint>
+
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+int RadmonInt(int max) {
+ return (rand() / 256) % max;
+}
+
+void CompareRegion(const DesktopRegion& region,
+ const DesktopRect rects[],
+ int rects_size) {
+ DesktopRegion::Iterator it(region);
+ for (int i = 0; i < rects_size; ++i) {
+ SCOPED_TRACE(i);
+ ASSERT_FALSE(it.IsAtEnd());
+ EXPECT_TRUE(it.rect().equals(rects[i]))
+ << it.rect().left() << "-" << it.rect().right() << "."
+ << it.rect().top() << "-" << it.rect().bottom() << " "
+ << rects[i].left() << "-" << rects[i].right() << "." << rects[i].top()
+ << "-" << rects[i].bottom();
+ it.Advance();
+ }
+ EXPECT_TRUE(it.IsAtEnd());
+}
+
+} // namespace
+
+// Verify that regions are empty when created.
+TEST(DesktopRegionTest, Empty) {
+ DesktopRegion r;
+ CompareRegion(r, NULL, 0);
+}
+
+// Verify that empty rectangles are ignored.
+TEST(DesktopRegionTest, AddEmpty) {
+ DesktopRegion r;
+ DesktopRect rect = DesktopRect::MakeXYWH(1, 2, 0, 0);
+ r.AddRect(rect);
+ CompareRegion(r, NULL, 0);
+}
+
+// Verify that regions with a single rectangles are handled properly.
+TEST(DesktopRegionTest, SingleRect) {
+ DesktopRegion r;
+ DesktopRect rect = DesktopRect::MakeXYWH(1, 2, 3, 4);
+ r.AddRect(rect);
+ CompareRegion(r, &rect, 1);
+}
+
+// Verify that non-overlapping rectangles are not merged.
+TEST(DesktopRegionTest, NonOverlappingRects) {
+ struct Case {
+ int count;
+ DesktopRect rects[4];
+ } cases[] = {
+ {1, {DesktopRect::MakeXYWH(10, 10, 10, 10)}},
+ {2,
+ {DesktopRect::MakeXYWH(10, 10, 10, 10),
+ DesktopRect::MakeXYWH(30, 10, 10, 15)}},
+ {2,
+ {DesktopRect::MakeXYWH(10, 10, 10, 10),
+ DesktopRect::MakeXYWH(10, 30, 10, 5)}},
+ {3,
+ {DesktopRect::MakeXYWH(10, 10, 10, 9),
+ DesktopRect::MakeXYWH(30, 10, 15, 10),
+ DesktopRect::MakeXYWH(10, 30, 8, 10)}},
+ {4,
+ {DesktopRect::MakeXYWH(0, 0, 30, 10),
+ DesktopRect::MakeXYWH(40, 0, 10, 30),
+ DesktopRect::MakeXYWH(0, 20, 10, 30),
+ DesktopRect::MakeXYWH(20, 40, 30, 10)}},
+ {4,
+ {DesktopRect::MakeXYWH(0, 0, 10, 100),
+ DesktopRect::MakeXYWH(20, 10, 30, 10),
+ DesktopRect::MakeXYWH(20, 30, 30, 10),
+ DesktopRect::MakeXYWH(20, 50, 30, 10)}},
+ };
+
+ for (size_t i = 0; i < (sizeof(cases) / sizeof(Case)); ++i) {
+ SCOPED_TRACE(i);
+
+ DesktopRegion r;
+
+ for (int j = 0; j < cases[i].count; ++j) {
+ r.AddRect(cases[i].rects[j]);
+ }
+ CompareRegion(r, cases[i].rects, cases[i].count);
+
+ SCOPED_TRACE("Reverse");
+
+ // Try inserting rects in reverse order.
+ r.Clear();
+ for (int j = cases[i].count - 1; j >= 0; --j) {
+ r.AddRect(cases[i].rects[j]);
+ }
+ CompareRegion(r, cases[i].rects, cases[i].count);
+ }
+}
+
+TEST(DesktopRegionTest, TwoRects) {
+ struct Case {
+ DesktopRect input_rect1;
+ DesktopRect input_rect2;
+ int expected_count;
+ DesktopRect expected_rects[3];
+ } cases[] = {
+ // Touching rectangles that merge into one.
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(0, 100, 100, 200),
+ 1,
+ {DesktopRect::MakeLTRB(0, 100, 200, 200)}},
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(100, 0, 200, 100),
+ 1,
+ {DesktopRect::MakeLTRB(100, 0, 200, 200)}},
+
+ // Rectangles touching on the vertical edge.
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(0, 150, 100, 250),
+ 3,
+ {DesktopRect::MakeLTRB(100, 100, 200, 150),
+ DesktopRect::MakeLTRB(0, 150, 200, 200),
+ DesktopRect::MakeLTRB(0, 200, 100, 250)}},
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(0, 50, 100, 150),
+ 3,
+ {DesktopRect::MakeLTRB(0, 50, 100, 100),
+ DesktopRect::MakeLTRB(0, 100, 200, 150),
+ DesktopRect::MakeLTRB(100, 150, 200, 200)}},
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(0, 120, 100, 180),
+ 3,
+ {DesktopRect::MakeLTRB(100, 100, 200, 120),
+ DesktopRect::MakeLTRB(0, 120, 200, 180),
+ DesktopRect::MakeLTRB(100, 180, 200, 200)}},
+
+ // Rectangles touching on the horizontal edge.
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(150, 0, 250, 100),
+ 2,
+ {DesktopRect::MakeLTRB(150, 0, 250, 100),
+ DesktopRect::MakeLTRB(100, 100, 200, 200)}},
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(50, 0, 150, 100),
+ 2,
+ {DesktopRect::MakeLTRB(50, 0, 150, 100),
+ DesktopRect::MakeLTRB(100, 100, 200, 200)}},
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(120, 0, 180, 100),
+ 2,
+ {DesktopRect::MakeLTRB(120, 0, 180, 100),
+ DesktopRect::MakeLTRB(100, 100, 200, 200)}},
+
+ // Overlapping rectangles.
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(50, 50, 150, 150),
+ 3,
+ {DesktopRect::MakeLTRB(50, 50, 150, 100),
+ DesktopRect::MakeLTRB(50, 100, 200, 150),
+ DesktopRect::MakeLTRB(100, 150, 200, 200)}},
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(150, 50, 250, 150),
+ 3,
+ {DesktopRect::MakeLTRB(150, 50, 250, 100),
+ DesktopRect::MakeLTRB(100, 100, 250, 150),
+ DesktopRect::MakeLTRB(100, 150, 200, 200)}},
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(0, 120, 150, 180),
+ 3,
+ {DesktopRect::MakeLTRB(100, 100, 200, 120),
+ DesktopRect::MakeLTRB(0, 120, 200, 180),
+ DesktopRect::MakeLTRB(100, 180, 200, 200)}},
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(120, 0, 180, 150),
+ 2,
+ {DesktopRect::MakeLTRB(120, 0, 180, 100),
+ DesktopRect::MakeLTRB(100, 100, 200, 200)}},
+ {DesktopRect::MakeLTRB(100, 0, 200, 300),
+ DesktopRect::MakeLTRB(0, 100, 300, 200),
+ 3,
+ {DesktopRect::MakeLTRB(100, 0, 200, 100),
+ DesktopRect::MakeLTRB(0, 100, 300, 200),
+ DesktopRect::MakeLTRB(100, 200, 200, 300)}},
+
+ // One rectangle enclosing another.
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(150, 150, 180, 180),
+ 1,
+ {DesktopRect::MakeLTRB(100, 100, 200, 200)}},
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(100, 100, 180, 180),
+ 1,
+ {DesktopRect::MakeLTRB(100, 100, 200, 200)}},
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(150, 150, 200, 200),
+ 1,
+ {DesktopRect::MakeLTRB(100, 100, 200, 200)}},
+ };
+
+ for (size_t i = 0; i < (sizeof(cases) / sizeof(Case)); ++i) {
+ SCOPED_TRACE(i);
+
+ DesktopRegion r;
+
+ r.AddRect(cases[i].input_rect1);
+ r.AddRect(cases[i].input_rect2);
+ CompareRegion(r, cases[i].expected_rects, cases[i].expected_count);
+
+ SCOPED_TRACE("Reverse");
+
+ // Run the same test with rectangles inserted in reverse order.
+ r.Clear();
+ r.AddRect(cases[i].input_rect2);
+ r.AddRect(cases[i].input_rect1);
+ CompareRegion(r, cases[i].expected_rects, cases[i].expected_count);
+ }
+}
+
+// Verify that DesktopRegion::AddRectToRow() works correctly by creating a row
+// of not overlapping rectangles and insert an overlapping rectangle into the
+// row at different positions. Result is verified by building a map of the
+// region in an array and comparing it with the expected values.
+TEST(DesktopRegionTest, SameRow) {
+ const int kMapWidth = 50;
+ const int kLastRectSizes[] = {3, 27};
+
+ DesktopRegion base_region;
+ bool base_map[kMapWidth] = {
+ false,
+ };
+
+ base_region.AddRect(DesktopRect::MakeXYWH(5, 0, 5, 1));
+ std::fill_n(base_map + 5, 5, true);
+ base_region.AddRect(DesktopRect::MakeXYWH(15, 0, 5, 1));
+ std::fill_n(base_map + 15, 5, true);
+ base_region.AddRect(DesktopRect::MakeXYWH(25, 0, 5, 1));
+ std::fill_n(base_map + 25, 5, true);
+ base_region.AddRect(DesktopRect::MakeXYWH(35, 0, 5, 1));
+ std::fill_n(base_map + 35, 5, true);
+ base_region.AddRect(DesktopRect::MakeXYWH(45, 0, 5, 1));
+ std::fill_n(base_map + 45, 5, true);
+
+ for (size_t i = 0; i < sizeof(kLastRectSizes) / sizeof(kLastRectSizes[0]);
+ i++) {
+ int last_rect_size = kLastRectSizes[i];
+ for (int x = 0; x < kMapWidth - last_rect_size; x++) {
+ SCOPED_TRACE(x);
+
+ DesktopRegion r = base_region;
+ r.AddRect(DesktopRect::MakeXYWH(x, 0, last_rect_size, 1));
+
+ bool expected_map[kMapWidth];
+ std::copy(base_map, base_map + kMapWidth, expected_map);
+ std::fill_n(expected_map + x, last_rect_size, true);
+
+ bool map[kMapWidth] = {
+ false,
+ };
+
+ int pos = -1;
+ for (DesktopRegion::Iterator it(r); !it.IsAtEnd(); it.Advance()) {
+ EXPECT_GT(it.rect().left(), pos);
+ pos = it.rect().right();
+ std::fill_n(map + it.rect().left(), it.rect().width(), true);
+ }
+
+ EXPECT_TRUE(std::equal(map, map + kMapWidth, expected_map));
+ }
+ }
+}
+
+TEST(DesktopRegionTest, ComplexRegions) {
+ struct Case {
+ int input_count;
+ DesktopRect input_rects[4];
+ int expected_count;
+ DesktopRect expected_rects[6];
+ } cases[] = {
+ {3,
+ {
+ DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(0, 100, 100, 200),
+ DesktopRect::MakeLTRB(310, 110, 320, 120),
+ },
+ 2,
+ {DesktopRect::MakeLTRB(0, 100, 200, 200),
+ DesktopRect::MakeLTRB(310, 110, 320, 120)}},
+ {3,
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(50, 50, 150, 150),
+ DesktopRect::MakeLTRB(300, 125, 350, 175)},
+ 4,
+ {DesktopRect::MakeLTRB(50, 50, 150, 100),
+ DesktopRect::MakeLTRB(50, 100, 200, 150),
+ DesktopRect::MakeLTRB(300, 125, 350, 175),
+ DesktopRect::MakeLTRB(100, 150, 200, 200)}},
+ {4,
+ {DesktopRect::MakeLTRB(0, 0, 30, 30),
+ DesktopRect::MakeLTRB(10, 10, 40, 40),
+ DesktopRect::MakeLTRB(20, 20, 50, 50),
+ DesktopRect::MakeLTRB(50, 0, 65, 15)},
+ 6,
+ {DesktopRect::MakeLTRB(0, 0, 30, 10),
+ DesktopRect::MakeLTRB(50, 0, 65, 15),
+ DesktopRect::MakeLTRB(0, 10, 40, 20),
+ DesktopRect::MakeLTRB(0, 20, 50, 30),
+ DesktopRect::MakeLTRB(10, 30, 50, 40),
+ DesktopRect::MakeLTRB(20, 40, 50, 50)}},
+ {3,
+ {DesktopRect::MakeLTRB(10, 10, 40, 20),
+ DesktopRect::MakeLTRB(10, 30, 40, 40),
+ DesktopRect::MakeLTRB(10, 20, 40, 30)},
+ 1,
+ {DesktopRect::MakeLTRB(10, 10, 40, 40)}},
+ };
+
+ for (size_t i = 0; i < (sizeof(cases) / sizeof(Case)); ++i) {
+ SCOPED_TRACE(i);
+
+ DesktopRegion r;
+ r.AddRects(cases[i].input_rects, cases[i].input_count);
+ CompareRegion(r, cases[i].expected_rects, cases[i].expected_count);
+
+ // Try inserting rectangles in reverse order.
+ r.Clear();
+ for (int j = cases[i].input_count - 1; j >= 0; --j) {
+ r.AddRect(cases[i].input_rects[j]);
+ }
+ CompareRegion(r, cases[i].expected_rects, cases[i].expected_count);
+ }
+}
+
+TEST(DesktopRegionTest, Equals) {
+ struct Region {
+ int count;
+ DesktopRect rects[4];
+ int id;
+ } regions[] = {
+ // Same region with one of the rectangles 1 pixel wider/taller.
+ {2,
+ {DesktopRect::MakeLTRB(0, 100, 200, 200),
+ DesktopRect::MakeLTRB(310, 110, 320, 120)},
+ 0},
+ {2,
+ {DesktopRect::MakeLTRB(0, 100, 201, 200),
+ DesktopRect::MakeLTRB(310, 110, 320, 120)},
+ 1},
+ {2,
+ {DesktopRect::MakeLTRB(0, 100, 200, 201),
+ DesktopRect::MakeLTRB(310, 110, 320, 120)},
+ 2},
+
+ // Same region with one of the rectangles shifted horizontally and
+ // vertically.
+ {4,
+ {DesktopRect::MakeLTRB(0, 0, 30, 30),
+ DesktopRect::MakeLTRB(10, 10, 40, 40),
+ DesktopRect::MakeLTRB(20, 20, 50, 50),
+ DesktopRect::MakeLTRB(50, 0, 65, 15)},
+ 3},
+ {4,
+ {DesktopRect::MakeLTRB(0, 0, 30, 30),
+ DesktopRect::MakeLTRB(10, 10, 40, 40),
+ DesktopRect::MakeLTRB(20, 20, 50, 50),
+ DesktopRect::MakeLTRB(50, 1, 65, 16)},
+ 4},
+ {4,
+ {DesktopRect::MakeLTRB(0, 0, 30, 30),
+ DesktopRect::MakeLTRB(10, 10, 40, 40),
+ DesktopRect::MakeLTRB(20, 20, 50, 50),
+ DesktopRect::MakeLTRB(51, 0, 66, 15)},
+ 5},
+
+ // Same region defined by a different set of rectangles - one of the
+ // rectangle is split horizontally into two.
+ {3,
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(50, 50, 150, 150),
+ DesktopRect::MakeLTRB(300, 125, 350, 175)},
+ 6},
+ {4,
+ {DesktopRect::MakeLTRB(100, 100, 200, 200),
+ DesktopRect::MakeLTRB(50, 50, 100, 150),
+ DesktopRect::MakeLTRB(100, 50, 150, 150),
+ DesktopRect::MakeLTRB(300, 125, 350, 175)},
+ 6},
+
+ // Rectangle region defined by a set of rectangles that merge into one.
+ {3,
+ {DesktopRect::MakeLTRB(10, 10, 40, 20),
+ DesktopRect::MakeLTRB(10, 30, 40, 40),
+ DesktopRect::MakeLTRB(10, 20, 40, 30)},
+ 7},
+ {1, {DesktopRect::MakeLTRB(10, 10, 40, 40)}, 7},
+ };
+ int kTotalRegions = sizeof(regions) / sizeof(Region);
+
+ for (int i = 0; i < kTotalRegions; ++i) {
+ SCOPED_TRACE(i);
+
+ DesktopRegion r1(regions[i].rects, regions[i].count);
+ for (int j = 0; j < kTotalRegions; ++j) {
+ SCOPED_TRACE(j);
+
+ DesktopRegion r2(regions[j].rects, regions[j].count);
+ EXPECT_EQ(regions[i].id == regions[j].id, r1.Equals(r2));
+ }
+ }
+}
+
+TEST(DesktopRegionTest, Translate) {
+ struct Case {
+ int input_count;
+ DesktopRect input_rects[4];
+ int dx;
+ int dy;
+ int expected_count;
+ DesktopRect expected_rects[5];
+ } cases[] = {
+ {3,
+ {DesktopRect::MakeLTRB(0, 0, 30, 30),
+ DesktopRect::MakeLTRB(10, 10, 40, 40),
+ DesktopRect::MakeLTRB(20, 20, 50, 50)},
+ 3,
+ 5,
+ 5,
+ {DesktopRect::MakeLTRB(3, 5, 33, 15),
+ DesktopRect::MakeLTRB(3, 15, 43, 25),
+ DesktopRect::MakeLTRB(3, 25, 53, 35),
+ DesktopRect::MakeLTRB(13, 35, 53, 45),
+ DesktopRect::MakeLTRB(23, 45, 53, 55)}},
+ };
+
+ for (size_t i = 0; i < (sizeof(cases) / sizeof(Case)); ++i) {
+ SCOPED_TRACE(i);
+
+ DesktopRegion r(cases[i].input_rects, cases[i].input_count);
+ r.Translate(cases[i].dx, cases[i].dy);
+ CompareRegion(r, cases[i].expected_rects, cases[i].expected_count);
+ }
+}
+
+TEST(DesktopRegionTest, Intersect) {
+ struct Case {
+ int input1_count;
+ DesktopRect input1_rects[4];
+ int input2_count;
+ DesktopRect input2_rects[4];
+ int expected_count;
+ DesktopRect expected_rects[5];
+ } cases[] = {
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(50, 50, 150, 150)},
+ 1,
+ {DesktopRect::MakeLTRB(50, 50, 100, 100)}},
+
+ {1,
+ {DesktopRect::MakeLTRB(100, 0, 200, 300)},
+ 1,
+ {DesktopRect::MakeLTRB(0, 100, 300, 200)},
+ 1,
+ {DesktopRect::MakeLTRB(100, 100, 200, 200)}},
+
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 2,
+ {DesktopRect::MakeLTRB(50, 10, 150, 30),
+ DesktopRect::MakeLTRB(50, 30, 160, 50)},
+ 1,
+ {DesktopRect::MakeLTRB(50, 10, 100, 50)}},
+
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 2,
+ {DesktopRect::MakeLTRB(50, 10, 150, 30),
+ DesktopRect::MakeLTRB(50, 30, 90, 50)},
+ 2,
+ {DesktopRect::MakeLTRB(50, 10, 100, 30),
+ DesktopRect::MakeLTRB(50, 30, 90, 50)}},
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(100, 50, 200, 200)},
+ 0,
+ {}},
+ };
+
+ for (size_t i = 0; i < (sizeof(cases) / sizeof(Case)); ++i) {
+ SCOPED_TRACE(i);
+
+ DesktopRegion r1(cases[i].input1_rects, cases[i].input1_count);
+ DesktopRegion r2(cases[i].input2_rects, cases[i].input2_count);
+
+ DesktopRegion r;
+ r.Intersect(r1, r2);
+
+ CompareRegion(r, cases[i].expected_rects, cases[i].expected_count);
+ }
+}
+
+TEST(DesktopRegionTest, Subtract) {
+ struct Case {
+ int input1_count;
+ DesktopRect input1_rects[4];
+ int input2_count;
+ DesktopRect input2_rects[4];
+ int expected_count;
+ DesktopRect expected_rects[5];
+ } cases[] = {
+ // Subtract one rect from another.
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(50, 50, 150, 150)},
+ 2,
+ {DesktopRect::MakeLTRB(0, 0, 100, 50),
+ DesktopRect::MakeLTRB(0, 50, 50, 100)}},
+
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(-50, -50, 50, 50)},
+ 2,
+ {DesktopRect::MakeLTRB(50, 0, 100, 50),
+ DesktopRect::MakeLTRB(0, 50, 100, 100)}},
+
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(-50, 50, 50, 150)},
+ 2,
+ {DesktopRect::MakeLTRB(0, 0, 100, 50),
+ DesktopRect::MakeLTRB(50, 50, 100, 100)}},
+
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(50, 50, 150, 70)},
+ 3,
+ {DesktopRect::MakeLTRB(0, 0, 100, 50),
+ DesktopRect::MakeLTRB(0, 50, 50, 70),
+ DesktopRect::MakeLTRB(0, 70, 100, 100)}},
+
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(50, 50, 70, 70)},
+ 4,
+ {DesktopRect::MakeLTRB(0, 0, 100, 50),
+ DesktopRect::MakeLTRB(0, 50, 50, 70),
+ DesktopRect::MakeLTRB(70, 50, 100, 70),
+ DesktopRect::MakeLTRB(0, 70, 100, 100)}},
+
+ // Empty result.
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 0,
+ {}},
+
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(-10, -10, 110, 110)},
+ 0,
+ {}},
+
+ {2,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100),
+ DesktopRect::MakeLTRB(50, 50, 150, 150)},
+ 2,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100),
+ DesktopRect::MakeLTRB(50, 50, 150, 150)},
+ 0,
+ {}},
+
+ // One rect out of disjoint set.
+ {3,
+ {DesktopRect::MakeLTRB(0, 0, 10, 10),
+ DesktopRect::MakeLTRB(20, 20, 30, 30),
+ DesktopRect::MakeLTRB(40, 0, 50, 10)},
+ 1,
+ {DesktopRect::MakeLTRB(20, 20, 30, 30)},
+ 2,
+ {DesktopRect::MakeLTRB(0, 0, 10, 10),
+ DesktopRect::MakeLTRB(40, 0, 50, 10)}},
+
+ // Row merging.
+ {3,
+ {DesktopRect::MakeLTRB(0, 0, 100, 50),
+ DesktopRect::MakeLTRB(0, 50, 150, 70),
+ DesktopRect::MakeLTRB(0, 70, 100, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(100, 50, 150, 70)},
+ 1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)}},
+
+ // No-op subtraction.
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(100, 0, 200, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)}},
+
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(-100, 0, 0, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)}},
+
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(0, 100, 0, 200)},
+ 1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)}},
+
+ {1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)},
+ 1,
+ {DesktopRect::MakeLTRB(0, -100, 100, 0)},
+ 1,
+ {DesktopRect::MakeLTRB(0, 0, 100, 100)}},
+ };
+
+ for (size_t i = 0; i < (sizeof(cases) / sizeof(Case)); ++i) {
+ SCOPED_TRACE(i);
+
+ DesktopRegion r1(cases[i].input1_rects, cases[i].input1_count);
+ DesktopRegion r2(cases[i].input2_rects, cases[i].input2_count);
+
+ r1.Subtract(r2);
+
+ CompareRegion(r1, cases[i].expected_rects, cases[i].expected_count);
+ }
+}
+
+// Verify that DesktopRegion::SubtractRows() works correctly by creating a row
+// of not overlapping rectangles and subtracting a set of rectangle. Result
+// is verified by building a map of the region in an array and comparing it with
+// the expected values.
+TEST(DesktopRegionTest, SubtractRectOnSameRow) {
+ const int kMapWidth = 50;
+
+ struct SpanSet {
+ int count;
+ struct Range {
+ int start;
+ int end;
+ } spans[3];
+ } span_sets[] = {
+ {1, {{0, 3}}},
+ {1, {{0, 5}}},
+ {1, {{0, 7}}},
+ {1, {{0, 12}}},
+ {2, {{0, 3}, {4, 5}, {6, 16}}},
+ };
+
+ DesktopRegion base_region;
+ bool base_map[kMapWidth] = {
+ false,
+ };
+
+ base_region.AddRect(DesktopRect::MakeXYWH(5, 0, 5, 1));
+ std::fill_n(base_map + 5, 5, true);
+ base_region.AddRect(DesktopRect::MakeXYWH(15, 0, 5, 1));
+ std::fill_n(base_map + 15, 5, true);
+ base_region.AddRect(DesktopRect::MakeXYWH(25, 0, 5, 1));
+ std::fill_n(base_map + 25, 5, true);
+ base_region.AddRect(DesktopRect::MakeXYWH(35, 0, 5, 1));
+ std::fill_n(base_map + 35, 5, true);
+ base_region.AddRect(DesktopRect::MakeXYWH(45, 0, 5, 1));
+ std::fill_n(base_map + 45, 5, true);
+
+ for (size_t i = 0; i < sizeof(span_sets) / sizeof(span_sets[0]); i++) {
+ SCOPED_TRACE(i);
+ SpanSet& span_set = span_sets[i];
+ int span_set_end = span_set.spans[span_set.count - 1].end;
+ for (int x = 0; x < kMapWidth - span_set_end; ++x) {
+ SCOPED_TRACE(x);
+
+ DesktopRegion r = base_region;
+
+ bool expected_map[kMapWidth];
+ std::copy(base_map, base_map + kMapWidth, expected_map);
+
+ DesktopRegion region2;
+ for (int span = 0; span < span_set.count; span++) {
+ std::fill_n(x + expected_map + span_set.spans[span].start,
+ span_set.spans[span].end - span_set.spans[span].start,
+ false);
+ region2.AddRect(DesktopRect::MakeLTRB(x + span_set.spans[span].start, 0,
+ x + span_set.spans[span].end, 1));
+ }
+ r.Subtract(region2);
+
+ bool map[kMapWidth] = {
+ false,
+ };
+
+ int pos = -1;
+ for (DesktopRegion::Iterator it(r); !it.IsAtEnd(); it.Advance()) {
+ EXPECT_GT(it.rect().left(), pos);
+ pos = it.rect().right();
+ std::fill_n(map + it.rect().left(), it.rect().width(), true);
+ }
+
+ EXPECT_TRUE(std::equal(map, map + kMapWidth, expected_map));
+ }
+ }
+}
+
+// Verify that DesktopRegion::Subtract() works correctly by creating a column of
+// not overlapping rectangles and subtracting a set of rectangle on the same
+// column. Result is verified by building a map of the region in an array and
+// comparing it with the expected values.
+TEST(DesktopRegionTest, SubtractRectOnSameCol) {
+ const int kMapHeight = 50;
+
+ struct SpanSet {
+ int count;
+ struct Range {
+ int start;
+ int end;
+ } spans[3];
+ } span_sets[] = {
+ {1, {{0, 3}}},
+ {1, {{0, 5}}},
+ {1, {{0, 7}}},
+ {1, {{0, 12}}},
+ {2, {{0, 3}, {4, 5}, {6, 16}}},
+ };
+
+ DesktopRegion base_region;
+ bool base_map[kMapHeight] = {
+ false,
+ };
+
+ base_region.AddRect(DesktopRect::MakeXYWH(0, 5, 1, 5));
+ std::fill_n(base_map + 5, 5, true);
+ base_region.AddRect(DesktopRect::MakeXYWH(0, 15, 1, 5));
+ std::fill_n(base_map + 15, 5, true);
+ base_region.AddRect(DesktopRect::MakeXYWH(0, 25, 1, 5));
+ std::fill_n(base_map + 25, 5, true);
+ base_region.AddRect(DesktopRect::MakeXYWH(0, 35, 1, 5));
+ std::fill_n(base_map + 35, 5, true);
+ base_region.AddRect(DesktopRect::MakeXYWH(0, 45, 1, 5));
+ std::fill_n(base_map + 45, 5, true);
+
+ for (size_t i = 0; i < sizeof(span_sets) / sizeof(span_sets[0]); i++) {
+ SCOPED_TRACE(i);
+ SpanSet& span_set = span_sets[i];
+ int span_set_end = span_set.spans[span_set.count - 1].end;
+ for (int y = 0; y < kMapHeight - span_set_end; ++y) {
+ SCOPED_TRACE(y);
+
+ DesktopRegion r = base_region;
+
+ bool expected_map[kMapHeight];
+ std::copy(base_map, base_map + kMapHeight, expected_map);
+
+ DesktopRegion region2;
+ for (int span = 0; span < span_set.count; span++) {
+ std::fill_n(y + expected_map + span_set.spans[span].start,
+ span_set.spans[span].end - span_set.spans[span].start,
+ false);
+ region2.AddRect(DesktopRect::MakeLTRB(0, y + span_set.spans[span].start,
+ 1, y + span_set.spans[span].end));
+ }
+ r.Subtract(region2);
+
+ bool map[kMapHeight] = {
+ false,
+ };
+
+ int pos = -1;
+ for (DesktopRegion::Iterator it(r); !it.IsAtEnd(); it.Advance()) {
+ EXPECT_GT(it.rect().top(), pos);
+ pos = it.rect().bottom();
+ std::fill_n(map + it.rect().top(), it.rect().height(), true);
+ }
+
+ for (int j = 0; j < kMapHeight; j++) {
+ EXPECT_EQ(expected_map[j], map[j]) << "j = " << j;
+ }
+ }
+ }
+}
+
+TEST(DesktopRegionTest, DISABLED_Performance) {
+ for (int c = 0; c < 1000; ++c) {
+ DesktopRegion r;
+ for (int i = 0; i < 10; ++i) {
+ r.AddRect(
+ DesktopRect::MakeXYWH(RadmonInt(1000), RadmonInt(1000), 200, 200));
+ }
+
+ for (int i = 0; i < 1000; ++i) {
+ r.AddRect(DesktopRect::MakeXYWH(RadmonInt(1000), RadmonInt(1000),
+ 5 + RadmonInt(10) * 5,
+ 5 + RadmonInt(10) * 5));
+ }
+
+ // Iterate over the rectangles.
+ for (DesktopRegion::Iterator it(r); !it.IsAtEnd(); it.Advance()) {
+ }
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/differ_block.cc b/third_party/libwebrtc/modules/desktop_capture/differ_block.cc
new file mode 100644
index 0000000000..54ee0829ea
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/differ_block.cc
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/differ_block.h"
+
+#include <string.h>
+
+#include "modules/desktop_capture/differ_vector_sse2.h"
+#include "rtc_base/system/arch.h"
+#include "system_wrappers/include/cpu_features_wrapper.h"
+
+namespace webrtc {
+
+namespace {
+
+bool VectorDifference_C(const uint8_t* image1, const uint8_t* image2) {
+ return memcmp(image1, image2, kBlockSize * kBytesPerPixel) != 0;
+}
+
+} // namespace
+
+bool VectorDifference(const uint8_t* image1, const uint8_t* image2) {
+ static bool (*diff_proc)(const uint8_t*, const uint8_t*) = nullptr;
+
+ if (!diff_proc) {
+#if defined(WEBRTC_ARCH_X86_FAMILY)
+ bool have_sse2 = GetCPUInfo(kSSE2) != 0;
+ // For x86 processors, check if SSE2 is supported.
+ if (have_sse2 && kBlockSize == 32) {
+ diff_proc = &VectorDifference_SSE2_W32;
+ } else if (have_sse2 && kBlockSize == 16) {
+ diff_proc = &VectorDifference_SSE2_W16;
+ } else {
+ diff_proc = &VectorDifference_C;
+ }
+#else
+ // For other processors, always use C version.
+ // TODO(hclam): Implement a NEON version.
+ diff_proc = &VectorDifference_C;
+#endif
+ }
+
+ return diff_proc(image1, image2);
+}
+
+bool BlockDifference(const uint8_t* image1,
+ const uint8_t* image2,
+ int height,
+ int stride) {
+ for (int i = 0; i < height; i++) {
+ if (VectorDifference(image1, image2)) {
+ return true;
+ }
+ image1 += stride;
+ image2 += stride;
+ }
+ return false;
+}
+
+bool BlockDifference(const uint8_t* image1, const uint8_t* image2, int stride) {
+ return BlockDifference(image1, image2, kBlockSize, stride);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/differ_block.h b/third_party/libwebrtc/modules/desktop_capture/differ_block.h
new file mode 100644
index 0000000000..6c71e214e9
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/differ_block.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_DIFFER_BLOCK_H_
+#define MODULES_DESKTOP_CAPTURE_DIFFER_BLOCK_H_
+
+#include <stdint.h>
+
+namespace webrtc {
+
+// Size (in pixels) of each square block used for diffing. This must be a
+// multiple of sizeof(uint64)/8.
+const int kBlockSize = 32;
+
+// Format: BGRA 32 bit.
+const int kBytesPerPixel = 4;
+
+// Low level function to compare 2 vectors of pixels of size kBlockSize. Returns
+// whether the blocks differ.
+bool VectorDifference(const uint8_t* image1, const uint8_t* image2);
+
+// Low level function to compare 2 blocks of pixels of size
+// (kBlockSize, `height`). Returns whether the blocks differ.
+bool BlockDifference(const uint8_t* image1,
+ const uint8_t* image2,
+ int height,
+ int stride);
+
+// Low level function to compare 2 blocks of pixels of size
+// (kBlockSize, kBlockSize). Returns whether the blocks differ.
+bool BlockDifference(const uint8_t* image1, const uint8_t* image2, int stride);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DIFFER_BLOCK_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/differ_block_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/differ_block_unittest.cc
new file mode 100644
index 0000000000..aa454c872d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/differ_block_unittest.cc
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/differ_block.h"
+
+#include <string.h>
+
+#include "test/gtest.h"
+
+namespace webrtc {
+
+// Run 900 times to mimic 1280x720.
+// TODO(fbarchard): Remove benchmark once performance is non-issue.
+static const int kTimesToRun = 900;
+
+static void GenerateData(uint8_t* data, int size) {
+ for (int i = 0; i < size; ++i) {
+ data[i] = i;
+ }
+}
+
+// Memory buffer large enough for 2 blocks aligned to 16 bytes.
+static const int kSizeOfBlock = kBlockSize * kBlockSize * kBytesPerPixel;
+uint8_t block_buffer[kSizeOfBlock * 2 + 16];
+
+void PrepareBuffers(uint8_t*& block1, uint8_t*& block2) {
+ block1 = reinterpret_cast<uint8_t*>(
+ (reinterpret_cast<uintptr_t>(&block_buffer[0]) + 15) & ~15);
+ GenerateData(block1, kSizeOfBlock);
+ block2 = block1 + kSizeOfBlock;
+ memcpy(block2, block1, kSizeOfBlock);
+}
+
+TEST(BlockDifferenceTestSame, BlockDifference) {
+ uint8_t* block1;
+ uint8_t* block2;
+ PrepareBuffers(block1, block2);
+
+ // These blocks should match.
+ for (int i = 0; i < kTimesToRun; ++i) {
+ int result = BlockDifference(block1, block2, kBlockSize * kBytesPerPixel);
+ EXPECT_EQ(0, result);
+ }
+}
+
+TEST(BlockDifferenceTestLast, BlockDifference) {
+ uint8_t* block1;
+ uint8_t* block2;
+ PrepareBuffers(block1, block2);
+ block2[kSizeOfBlock - 2] += 1;
+
+ for (int i = 0; i < kTimesToRun; ++i) {
+ int result = BlockDifference(block1, block2, kBlockSize * kBytesPerPixel);
+ EXPECT_EQ(1, result);
+ }
+}
+
+TEST(BlockDifferenceTestMid, BlockDifference) {
+ uint8_t* block1;
+ uint8_t* block2;
+ PrepareBuffers(block1, block2);
+ block2[kSizeOfBlock / 2 + 1] += 1;
+
+ for (int i = 0; i < kTimesToRun; ++i) {
+ int result = BlockDifference(block1, block2, kBlockSize * kBytesPerPixel);
+ EXPECT_EQ(1, result);
+ }
+}
+
+TEST(BlockDifferenceTestFirst, BlockDifference) {
+ uint8_t* block1;
+ uint8_t* block2;
+ PrepareBuffers(block1, block2);
+ block2[0] += 1;
+
+ for (int i = 0; i < kTimesToRun; ++i) {
+ int result = BlockDifference(block1, block2, kBlockSize * kBytesPerPixel);
+ EXPECT_EQ(1, result);
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/differ_vector_sse2.cc b/third_party/libwebrtc/modules/desktop_capture/differ_vector_sse2.cc
new file mode 100644
index 0000000000..1c8b602d71
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/differ_vector_sse2.cc
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/differ_vector_sse2.h"
+
+#if defined(_MSC_VER)
+#include <intrin.h>
+#else
+#include <emmintrin.h>
+#include <mmintrin.h>
+#endif
+
+namespace webrtc {
+
+extern bool VectorDifference_SSE2_W16(const uint8_t* image1,
+ const uint8_t* image2) {
+ __m128i acc = _mm_setzero_si128();
+ __m128i v0;
+ __m128i v1;
+ __m128i sad;
+ const __m128i* i1 = reinterpret_cast<const __m128i*>(image1);
+ const __m128i* i2 = reinterpret_cast<const __m128i*>(image2);
+ v0 = _mm_loadu_si128(i1);
+ v1 = _mm_loadu_si128(i2);
+ sad = _mm_sad_epu8(v0, v1);
+ acc = _mm_adds_epu16(acc, sad);
+ v0 = _mm_loadu_si128(i1 + 1);
+ v1 = _mm_loadu_si128(i2 + 1);
+ sad = _mm_sad_epu8(v0, v1);
+ acc = _mm_adds_epu16(acc, sad);
+ v0 = _mm_loadu_si128(i1 + 2);
+ v1 = _mm_loadu_si128(i2 + 2);
+ sad = _mm_sad_epu8(v0, v1);
+ acc = _mm_adds_epu16(acc, sad);
+ v0 = _mm_loadu_si128(i1 + 3);
+ v1 = _mm_loadu_si128(i2 + 3);
+ sad = _mm_sad_epu8(v0, v1);
+ acc = _mm_adds_epu16(acc, sad);
+
+ // This essential means sad = acc >> 64. We only care about the lower 16
+ // bits.
+ sad = _mm_shuffle_epi32(acc, 0xEE);
+ sad = _mm_adds_epu16(sad, acc);
+ return _mm_cvtsi128_si32(sad) != 0;
+}
+
+extern bool VectorDifference_SSE2_W32(const uint8_t* image1,
+ const uint8_t* image2) {
+ __m128i acc = _mm_setzero_si128();
+ __m128i v0;
+ __m128i v1;
+ __m128i sad;
+ const __m128i* i1 = reinterpret_cast<const __m128i*>(image1);
+ const __m128i* i2 = reinterpret_cast<const __m128i*>(image2);
+ v0 = _mm_loadu_si128(i1);
+ v1 = _mm_loadu_si128(i2);
+ sad = _mm_sad_epu8(v0, v1);
+ acc = _mm_adds_epu16(acc, sad);
+ v0 = _mm_loadu_si128(i1 + 1);
+ v1 = _mm_loadu_si128(i2 + 1);
+ sad = _mm_sad_epu8(v0, v1);
+ acc = _mm_adds_epu16(acc, sad);
+ v0 = _mm_loadu_si128(i1 + 2);
+ v1 = _mm_loadu_si128(i2 + 2);
+ sad = _mm_sad_epu8(v0, v1);
+ acc = _mm_adds_epu16(acc, sad);
+ v0 = _mm_loadu_si128(i1 + 3);
+ v1 = _mm_loadu_si128(i2 + 3);
+ sad = _mm_sad_epu8(v0, v1);
+ acc = _mm_adds_epu16(acc, sad);
+ v0 = _mm_loadu_si128(i1 + 4);
+ v1 = _mm_loadu_si128(i2 + 4);
+ sad = _mm_sad_epu8(v0, v1);
+ acc = _mm_adds_epu16(acc, sad);
+ v0 = _mm_loadu_si128(i1 + 5);
+ v1 = _mm_loadu_si128(i2 + 5);
+ sad = _mm_sad_epu8(v0, v1);
+ acc = _mm_adds_epu16(acc, sad);
+ v0 = _mm_loadu_si128(i1 + 6);
+ v1 = _mm_loadu_si128(i2 + 6);
+ sad = _mm_sad_epu8(v0, v1);
+ acc = _mm_adds_epu16(acc, sad);
+ v0 = _mm_loadu_si128(i1 + 7);
+ v1 = _mm_loadu_si128(i2 + 7);
+ sad = _mm_sad_epu8(v0, v1);
+ acc = _mm_adds_epu16(acc, sad);
+
+ // This essential means sad = acc >> 64. We only care about the lower 16
+ // bits.
+ sad = _mm_shuffle_epi32(acc, 0xEE);
+ sad = _mm_adds_epu16(sad, acc);
+ return _mm_cvtsi128_si32(sad) != 0;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/differ_vector_sse2.h b/third_party/libwebrtc/modules/desktop_capture/differ_vector_sse2.h
new file mode 100644
index 0000000000..a3c297eb9d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/differ_vector_sse2.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// This header file is used only differ_block.h. It defines the SSE2 rountines
+// for finding vector difference.
+
+#ifndef MODULES_DESKTOP_CAPTURE_DIFFER_VECTOR_SSE2_H_
+#define MODULES_DESKTOP_CAPTURE_DIFFER_VECTOR_SSE2_H_
+
+#include <stdint.h>
+
+namespace webrtc {
+
+// Find vector difference of dimension 16.
+extern bool VectorDifference_SSE2_W16(const uint8_t* image1,
+ const uint8_t* image2);
+
+// Find vector difference of dimension 32.
+extern bool VectorDifference_SSE2_W32(const uint8_t* image1,
+ const uint8_t* image2);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_DIFFER_VECTOR_SSE2_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/fake_desktop_capturer.cc b/third_party/libwebrtc/modules/desktop_capture/fake_desktop_capturer.cc
new file mode 100644
index 0000000000..67149bfcb9
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/fake_desktop_capturer.cc
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/fake_desktop_capturer.h"
+
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_frame.h"
+
+namespace webrtc {
+
+FakeDesktopCapturer::FakeDesktopCapturer() = default;
+FakeDesktopCapturer::~FakeDesktopCapturer() = default;
+
+void FakeDesktopCapturer::set_result(DesktopCapturer::Result result) {
+ result_ = result;
+}
+
+int FakeDesktopCapturer::num_frames_captured() const {
+ return num_frames_captured_;
+}
+
+int FakeDesktopCapturer::num_capture_attempts() const {
+ return num_capture_attempts_;
+}
+
+// Uses the `generator` provided as DesktopFrameGenerator, FakeDesktopCapturer
+// does
+// not take the ownership of `generator`.
+void FakeDesktopCapturer::set_frame_generator(
+ DesktopFrameGenerator* generator) {
+ generator_ = generator;
+}
+
+void FakeDesktopCapturer::Start(DesktopCapturer::Callback* callback) {
+ callback_ = callback;
+}
+
+void FakeDesktopCapturer::CaptureFrame() {
+ num_capture_attempts_++;
+ if (generator_) {
+ if (result_ != DesktopCapturer::Result::SUCCESS) {
+ callback_->OnCaptureResult(result_, nullptr);
+ return;
+ }
+
+ std::unique_ptr<DesktopFrame> frame(
+ generator_->GetNextFrame(shared_memory_factory_.get()));
+ if (frame) {
+ num_frames_captured_++;
+ callback_->OnCaptureResult(result_, std::move(frame));
+ } else {
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_TEMPORARY,
+ nullptr);
+ }
+ return;
+ }
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, nullptr);
+}
+
+void FakeDesktopCapturer::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
+ shared_memory_factory_ = std::move(shared_memory_factory);
+}
+
+bool FakeDesktopCapturer::GetSourceList(DesktopCapturer::SourceList* sources) {
+ sources->push_back({kWindowId, 1, "A-Fake-DesktopCapturer-Window"});
+ sources->push_back({kScreenId, 1});
+ return true;
+}
+
+bool FakeDesktopCapturer::SelectSource(DesktopCapturer::SourceId id) {
+ return id == kWindowId || id == kScreenId || id == kFullDesktopScreenId;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/fake_desktop_capturer.h b/third_party/libwebrtc/modules/desktop_capture/fake_desktop_capturer.h
new file mode 100644
index 0000000000..086e6df0e2
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/fake_desktop_capturer.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_FAKE_DESKTOP_CAPTURER_H_
+#define MODULES_DESKTOP_CAPTURE_FAKE_DESKTOP_CAPTURER_H_
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame_generator.h"
+#include "modules/desktop_capture/shared_memory.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// A fake implementation of DesktopCapturer or its derived interfaces to
+// generate DesktopFrame for testing purpose.
+//
+// Consumers can provide a FrameGenerator instance to generate instances of
+// DesktopFrame to return for each Capture() function call.
+// If no FrameGenerator provided, FakeDesktopCapturer will always return a
+// nullptr DesktopFrame.
+//
+// Double buffering is guaranteed by the FrameGenerator. FrameGenerator
+// implements in desktop_frame_generator.h guarantee double buffering, they
+// creates a new instance of DesktopFrame each time.
+class RTC_EXPORT FakeDesktopCapturer : public DesktopCapturer {
+ public:
+ FakeDesktopCapturer();
+ ~FakeDesktopCapturer() override;
+
+ // Decides the result which will be returned in next Capture() callback.
+ void set_result(DesktopCapturer::Result result);
+
+ // Uses the `generator` provided as DesktopFrameGenerator, FakeDesktopCapturer
+ // does not take the ownership of `generator`.
+ void set_frame_generator(DesktopFrameGenerator* generator);
+
+ // Count of DesktopFrame(s) have been returned by this instance. This field
+ // would never be negative.
+ int num_frames_captured() const;
+
+ // Count of CaptureFrame() calls have been made. This field would never be
+ // negative.
+ int num_capture_attempts() const;
+
+ // DesktopCapturer interface
+ void Start(DesktopCapturer::Callback* callback) override;
+ void CaptureFrame() override;
+ void SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) override;
+ bool GetSourceList(DesktopCapturer::SourceList* sources) override;
+ bool SelectSource(DesktopCapturer::SourceId id) override;
+
+ private:
+ static constexpr DesktopCapturer::SourceId kWindowId = 1378277495;
+ static constexpr DesktopCapturer::SourceId kScreenId = 1378277496;
+
+ DesktopCapturer::Callback* callback_ = nullptr;
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;
+ DesktopCapturer::Result result_ = Result::SUCCESS;
+ DesktopFrameGenerator* generator_ = nullptr;
+ int num_frames_captured_ = 0;
+ int num_capture_attempts_ = 0;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_FAKE_DESKTOP_CAPTURER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/fallback_desktop_capturer_wrapper.cc b/third_party/libwebrtc/modules/desktop_capture/fallback_desktop_capturer_wrapper.cc
new file mode 100644
index 0000000000..61fdb416f2
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/fallback_desktop_capturer_wrapper.cc
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/fallback_desktop_capturer_wrapper.h"
+
+#include <stddef.h>
+
+#include <utility>
+
+#include "api/sequence_checker.h"
+#include "rtc_base/checks.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+
+namespace {
+
+// Implementation to share a SharedMemoryFactory between DesktopCapturer
+// instances. This class is designed for synchronized DesktopCapturer
+// implementations only.
+class SharedMemoryFactoryProxy : public SharedMemoryFactory {
+ public:
+ // Users should maintain the lifetime of `factory` to ensure it overlives
+ // current instance.
+ static std::unique_ptr<SharedMemoryFactory> Create(
+ SharedMemoryFactory* factory);
+ ~SharedMemoryFactoryProxy() override;
+
+ // Forwards CreateSharedMemory() calls to `factory_`. Users should always call
+ // this function in one thread. Users should not call this function after the
+ // SharedMemoryFactory which current instance created from has been destroyed.
+ std::unique_ptr<SharedMemory> CreateSharedMemory(size_t size) override;
+
+ private:
+ explicit SharedMemoryFactoryProxy(SharedMemoryFactory* factory);
+
+ SharedMemoryFactory* factory_ = nullptr;
+ SequenceChecker thread_checker_;
+};
+
+} // namespace
+
+SharedMemoryFactoryProxy::SharedMemoryFactoryProxy(
+ SharedMemoryFactory* factory) {
+ RTC_DCHECK(factory);
+ factory_ = factory;
+}
+
+// static
+std::unique_ptr<SharedMemoryFactory> SharedMemoryFactoryProxy::Create(
+ SharedMemoryFactory* factory) {
+ return std::unique_ptr<SharedMemoryFactory>(
+ new SharedMemoryFactoryProxy(factory));
+}
+
+SharedMemoryFactoryProxy::~SharedMemoryFactoryProxy() = default;
+
+std::unique_ptr<SharedMemory> SharedMemoryFactoryProxy::CreateSharedMemory(
+ size_t size) {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+ return factory_->CreateSharedMemory(size);
+}
+
+FallbackDesktopCapturerWrapper::FallbackDesktopCapturerWrapper(
+ std::unique_ptr<DesktopCapturer> main_capturer,
+ std::unique_ptr<DesktopCapturer> secondary_capturer)
+ : main_capturer_(std::move(main_capturer)),
+ secondary_capturer_(std::move(secondary_capturer)) {
+ RTC_DCHECK(main_capturer_);
+ RTC_DCHECK(secondary_capturer_);
+}
+
+FallbackDesktopCapturerWrapper::~FallbackDesktopCapturerWrapper() = default;
+
+void FallbackDesktopCapturerWrapper::Start(
+ DesktopCapturer::Callback* callback) {
+ callback_ = callback;
+ // FallbackDesktopCapturerWrapper catchs the callback of the main capturer,
+ // and checks its return value to decide whether the secondary capturer should
+ // be involved.
+ main_capturer_->Start(this);
+ // For the secondary capturer, we do not have a backup plan anymore, so
+ // FallbackDesktopCapturerWrapper won't check its return value any more. It
+ // will directly return to the input `callback`.
+ secondary_capturer_->Start(callback);
+}
+
+void FallbackDesktopCapturerWrapper::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
+ shared_memory_factory_ = std::move(shared_memory_factory);
+ if (shared_memory_factory_) {
+ main_capturer_->SetSharedMemoryFactory(
+ SharedMemoryFactoryProxy::Create(shared_memory_factory_.get()));
+ secondary_capturer_->SetSharedMemoryFactory(
+ SharedMemoryFactoryProxy::Create(shared_memory_factory_.get()));
+ } else {
+ main_capturer_->SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory>());
+ secondary_capturer_->SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory>());
+ }
+}
+
+void FallbackDesktopCapturerWrapper::CaptureFrame() {
+ RTC_DCHECK(callback_);
+ if (main_capturer_permanent_error_) {
+ secondary_capturer_->CaptureFrame();
+ } else {
+ main_capturer_->CaptureFrame();
+ }
+}
+
+void FallbackDesktopCapturerWrapper::SetExcludedWindow(WindowId window) {
+ main_capturer_->SetExcludedWindow(window);
+ secondary_capturer_->SetExcludedWindow(window);
+}
+
+bool FallbackDesktopCapturerWrapper::GetSourceList(SourceList* sources) {
+ if (main_capturer_permanent_error_) {
+ return secondary_capturer_->GetSourceList(sources);
+ }
+ return main_capturer_->GetSourceList(sources);
+}
+
+bool FallbackDesktopCapturerWrapper::SelectSource(SourceId id) {
+ if (main_capturer_permanent_error_) {
+ return secondary_capturer_->SelectSource(id);
+ }
+ const bool main_capturer_result = main_capturer_->SelectSource(id);
+ RTC_HISTOGRAM_BOOLEAN(
+ "WebRTC.DesktopCapture.PrimaryCapturerSelectSourceError",
+ main_capturer_result);
+ if (!main_capturer_result) {
+ main_capturer_permanent_error_ = true;
+ }
+
+ return secondary_capturer_->SelectSource(id);
+}
+
+bool FallbackDesktopCapturerWrapper::FocusOnSelectedSource() {
+ if (main_capturer_permanent_error_) {
+ return secondary_capturer_->FocusOnSelectedSource();
+ }
+ return main_capturer_->FocusOnSelectedSource() ||
+ secondary_capturer_->FocusOnSelectedSource();
+}
+
+bool FallbackDesktopCapturerWrapper::IsOccluded(const DesktopVector& pos) {
+ // Returns true if either capturer returns true.
+ if (main_capturer_permanent_error_) {
+ return secondary_capturer_->IsOccluded(pos);
+ }
+ return main_capturer_->IsOccluded(pos) ||
+ secondary_capturer_->IsOccluded(pos);
+}
+
+void FallbackDesktopCapturerWrapper::OnCaptureResult(
+ Result result,
+ std::unique_ptr<DesktopFrame> frame) {
+ RTC_DCHECK(callback_);
+ RTC_HISTOGRAM_BOOLEAN("WebRTC.DesktopCapture.PrimaryCapturerError",
+ result != Result::SUCCESS);
+ RTC_HISTOGRAM_BOOLEAN("WebRTC.DesktopCapture.PrimaryCapturerPermanentError",
+ result == Result::ERROR_PERMANENT);
+ if (result == Result::SUCCESS) {
+ callback_->OnCaptureResult(result, std::move(frame));
+ return;
+ }
+
+ if (result == Result::ERROR_PERMANENT) {
+ main_capturer_permanent_error_ = true;
+ }
+ secondary_capturer_->CaptureFrame();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/fallback_desktop_capturer_wrapper.h b/third_party/libwebrtc/modules/desktop_capture/fallback_desktop_capturer_wrapper.h
new file mode 100644
index 0000000000..2855eae7ee
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/fallback_desktop_capturer_wrapper.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_FALLBACK_DESKTOP_CAPTURER_WRAPPER_H_
+#define MODULES_DESKTOP_CAPTURE_FALLBACK_DESKTOP_CAPTURER_WRAPPER_H_
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/shared_memory.h"
+
+namespace webrtc {
+
+// A DesktopCapturer wrapper owns two DesktopCapturer implementations. If the
+// main DesktopCapturer fails, it uses the secondary one instead. Two capturers
+// are expected to return same SourceList, and the meaning of each SourceId is
+// identical, otherwise FallbackDesktopCapturerWrapper may return frames from
+// different sources. Using asynchronized DesktopCapturer implementations with
+// SharedMemoryFactory is not supported, and may result crash or assertion
+// failure.
+class FallbackDesktopCapturerWrapper final : public DesktopCapturer,
+ public DesktopCapturer::Callback {
+ public:
+ FallbackDesktopCapturerWrapper(
+ std::unique_ptr<DesktopCapturer> main_capturer,
+ std::unique_ptr<DesktopCapturer> secondary_capturer);
+ ~FallbackDesktopCapturerWrapper() override;
+
+ // DesktopCapturer interface.
+ void Start(DesktopCapturer::Callback* callback) override;
+ void SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) override;
+ void CaptureFrame() override;
+ void SetExcludedWindow(WindowId window) override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+ bool FocusOnSelectedSource() override;
+ bool IsOccluded(const DesktopVector& pos) override;
+
+ private:
+ // DesktopCapturer::Callback interface.
+ void OnCaptureResult(Result result,
+ std::unique_ptr<DesktopFrame> frame) override;
+
+ const std::unique_ptr<DesktopCapturer> main_capturer_;
+ const std::unique_ptr<DesktopCapturer> secondary_capturer_;
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;
+ bool main_capturer_permanent_error_ = false;
+ DesktopCapturer::Callback* callback_ = nullptr;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_FALLBACK_DESKTOP_CAPTURER_WRAPPER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/fallback_desktop_capturer_wrapper_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/fallback_desktop_capturer_wrapper_unittest.cc
new file mode 100644
index 0000000000..de66386434
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/fallback_desktop_capturer_wrapper_unittest.cc
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/fallback_desktop_capturer_wrapper.h"
+
+#include <stddef.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame_generator.h"
+#include "modules/desktop_capture/fake_desktop_capturer.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+std::unique_ptr<DesktopCapturer> CreateDesktopCapturer(
+ PainterDesktopFrameGenerator* frame_generator) {
+ std::unique_ptr<FakeDesktopCapturer> capturer(new FakeDesktopCapturer());
+ capturer->set_frame_generator(frame_generator);
+ return std::move(capturer);
+}
+
+class FakeSharedMemory : public SharedMemory {
+ public:
+ explicit FakeSharedMemory(size_t size);
+ ~FakeSharedMemory() override;
+
+ private:
+ static int next_id_;
+};
+
+// static
+int FakeSharedMemory::next_id_ = 0;
+
+FakeSharedMemory::FakeSharedMemory(size_t size)
+ : SharedMemory(new char[size], size, 0, next_id_++) {}
+
+FakeSharedMemory::~FakeSharedMemory() {
+ delete[] static_cast<char*>(data_);
+}
+
+class FakeSharedMemoryFactory : public SharedMemoryFactory {
+ public:
+ FakeSharedMemoryFactory() = default;
+ ~FakeSharedMemoryFactory() override = default;
+
+ std::unique_ptr<SharedMemory> CreateSharedMemory(size_t size) override;
+};
+
+std::unique_ptr<SharedMemory> FakeSharedMemoryFactory::CreateSharedMemory(
+ size_t size) {
+ return std::unique_ptr<SharedMemory>(new FakeSharedMemory(size));
+}
+
+} // namespace
+
+class FallbackDesktopCapturerWrapperTest : public ::testing::Test,
+ public DesktopCapturer::Callback {
+ public:
+ FallbackDesktopCapturerWrapperTest();
+ ~FallbackDesktopCapturerWrapperTest() override = default;
+
+ protected:
+ std::vector<std::pair<DesktopCapturer::Result, bool>> results_;
+ FakeDesktopCapturer* main_capturer_ = nullptr;
+ FakeDesktopCapturer* secondary_capturer_ = nullptr;
+ std::unique_ptr<FallbackDesktopCapturerWrapper> wrapper_;
+
+ private:
+ // DesktopCapturer::Callback interface
+ void OnCaptureResult(DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame> frame) override;
+ PainterDesktopFrameGenerator frame_generator_;
+};
+
+FallbackDesktopCapturerWrapperTest::FallbackDesktopCapturerWrapperTest() {
+ frame_generator_.size()->set(1024, 768);
+ std::unique_ptr<DesktopCapturer> main_capturer =
+ CreateDesktopCapturer(&frame_generator_);
+ std::unique_ptr<DesktopCapturer> secondary_capturer =
+ CreateDesktopCapturer(&frame_generator_);
+ main_capturer_ = static_cast<FakeDesktopCapturer*>(main_capturer.get());
+ secondary_capturer_ =
+ static_cast<FakeDesktopCapturer*>(secondary_capturer.get());
+ wrapper_.reset(new FallbackDesktopCapturerWrapper(
+ std::move(main_capturer), std::move(secondary_capturer)));
+ wrapper_->Start(this);
+}
+
+void FallbackDesktopCapturerWrapperTest::OnCaptureResult(
+ DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame> frame) {
+ results_.emplace_back(result, !!frame);
+}
+
+TEST_F(FallbackDesktopCapturerWrapperTest, MainNeverFailed) {
+ wrapper_->CaptureFrame();
+ ASSERT_EQ(main_capturer_->num_capture_attempts(), 1);
+ ASSERT_EQ(main_capturer_->num_frames_captured(), 1);
+ ASSERT_EQ(secondary_capturer_->num_capture_attempts(), 0);
+ ASSERT_EQ(secondary_capturer_->num_frames_captured(), 0);
+ ASSERT_EQ(results_.size(), 1U);
+ ASSERT_EQ(results_[0],
+ std::make_pair(DesktopCapturer::Result::SUCCESS, true));
+}
+
+TEST_F(FallbackDesktopCapturerWrapperTest, MainFailedTemporarily) {
+ wrapper_->CaptureFrame();
+ main_capturer_->set_result(DesktopCapturer::Result::ERROR_TEMPORARY);
+ wrapper_->CaptureFrame();
+ main_capturer_->set_result(DesktopCapturer::Result::SUCCESS);
+ wrapper_->CaptureFrame();
+
+ ASSERT_EQ(main_capturer_->num_capture_attempts(), 3);
+ ASSERT_EQ(main_capturer_->num_frames_captured(), 2);
+ ASSERT_EQ(secondary_capturer_->num_capture_attempts(), 1);
+ ASSERT_EQ(secondary_capturer_->num_frames_captured(), 1);
+ ASSERT_EQ(results_.size(), 3U);
+ for (int i = 0; i < 3; i++) {
+ ASSERT_EQ(results_[i],
+ std::make_pair(DesktopCapturer::Result::SUCCESS, true));
+ }
+}
+
+TEST_F(FallbackDesktopCapturerWrapperTest, MainFailedPermanently) {
+ wrapper_->CaptureFrame();
+ main_capturer_->set_result(DesktopCapturer::Result::ERROR_PERMANENT);
+ wrapper_->CaptureFrame();
+ main_capturer_->set_result(DesktopCapturer::Result::SUCCESS);
+ wrapper_->CaptureFrame();
+
+ ASSERT_EQ(main_capturer_->num_capture_attempts(), 2);
+ ASSERT_EQ(main_capturer_->num_frames_captured(), 1);
+ ASSERT_EQ(secondary_capturer_->num_capture_attempts(), 2);
+ ASSERT_EQ(secondary_capturer_->num_frames_captured(), 2);
+ ASSERT_EQ(results_.size(), 3U);
+ for (int i = 0; i < 3; i++) {
+ ASSERT_EQ(results_[i],
+ std::make_pair(DesktopCapturer::Result::SUCCESS, true));
+ }
+}
+
+TEST_F(FallbackDesktopCapturerWrapperTest, BothFailed) {
+ wrapper_->CaptureFrame();
+ main_capturer_->set_result(DesktopCapturer::Result::ERROR_PERMANENT);
+ wrapper_->CaptureFrame();
+ main_capturer_->set_result(DesktopCapturer::Result::SUCCESS);
+ wrapper_->CaptureFrame();
+ secondary_capturer_->set_result(DesktopCapturer::Result::ERROR_TEMPORARY);
+ wrapper_->CaptureFrame();
+ secondary_capturer_->set_result(DesktopCapturer::Result::ERROR_PERMANENT);
+ wrapper_->CaptureFrame();
+ wrapper_->CaptureFrame();
+
+ ASSERT_EQ(main_capturer_->num_capture_attempts(), 2);
+ ASSERT_EQ(main_capturer_->num_frames_captured(), 1);
+ ASSERT_EQ(secondary_capturer_->num_capture_attempts(), 5);
+ ASSERT_EQ(secondary_capturer_->num_frames_captured(), 2);
+ ASSERT_EQ(results_.size(), 6U);
+ for (int i = 0; i < 3; i++) {
+ ASSERT_EQ(results_[i],
+ std::make_pair(DesktopCapturer::Result::SUCCESS, true));
+ }
+ ASSERT_EQ(results_[3],
+ std::make_pair(DesktopCapturer::Result::ERROR_TEMPORARY, false));
+ ASSERT_EQ(results_[4],
+ std::make_pair(DesktopCapturer::Result::ERROR_PERMANENT, false));
+ ASSERT_EQ(results_[5],
+ std::make_pair(DesktopCapturer::Result::ERROR_PERMANENT, false));
+}
+
+TEST_F(FallbackDesktopCapturerWrapperTest, WithSharedMemory) {
+ wrapper_->SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory>(new FakeSharedMemoryFactory()));
+ wrapper_->CaptureFrame();
+ main_capturer_->set_result(DesktopCapturer::Result::ERROR_TEMPORARY);
+ wrapper_->CaptureFrame();
+ main_capturer_->set_result(DesktopCapturer::Result::SUCCESS);
+ wrapper_->CaptureFrame();
+ main_capturer_->set_result(DesktopCapturer::Result::ERROR_PERMANENT);
+ wrapper_->CaptureFrame();
+ wrapper_->CaptureFrame();
+
+ ASSERT_EQ(main_capturer_->num_capture_attempts(), 4);
+ ASSERT_EQ(main_capturer_->num_frames_captured(), 2);
+ ASSERT_EQ(secondary_capturer_->num_capture_attempts(), 3);
+ ASSERT_EQ(secondary_capturer_->num_frames_captured(), 3);
+ ASSERT_EQ(results_.size(), 5U);
+ for (int i = 0; i < 5; i++) {
+ ASSERT_EQ(results_[i],
+ std::make_pair(DesktopCapturer::Result::SUCCESS, true));
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/full_screen_application_handler.cc b/third_party/libwebrtc/modules/desktop_capture/full_screen_application_handler.cc
new file mode 100644
index 0000000000..e0975570ba
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/full_screen_application_handler.cc
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/full_screen_application_handler.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+FullScreenApplicationHandler::FullScreenApplicationHandler(
+ DesktopCapturer::SourceId sourceId)
+ : source_id_(sourceId) {}
+
+DesktopCapturer::SourceId FullScreenApplicationHandler::FindFullScreenWindow(
+ const DesktopCapturer::SourceList&,
+ int64_t) const {
+ return 0;
+}
+
+DesktopCapturer::SourceId FullScreenApplicationHandler::GetSourceId() const {
+ return source_id_;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/full_screen_application_handler.h b/third_party/libwebrtc/modules/desktop_capture/full_screen_application_handler.h
new file mode 100644
index 0000000000..b7e097a474
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/full_screen_application_handler.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_FULL_SCREEN_APPLICATION_HANDLER_H_
+#define MODULES_DESKTOP_CAPTURE_FULL_SCREEN_APPLICATION_HANDLER_H_
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+
+namespace webrtc {
+
+// Base class for application specific handler to check criteria for switch to
+// full-screen mode and find if possible the full-screen window to share.
+// Supposed to be created and owned by platform specific
+// FullScreenWindowDetector.
+class FullScreenApplicationHandler {
+ public:
+ virtual ~FullScreenApplicationHandler() {}
+
+ FullScreenApplicationHandler(const FullScreenApplicationHandler&) = delete;
+ FullScreenApplicationHandler& operator=(const FullScreenApplicationHandler&) =
+ delete;
+
+ explicit FullScreenApplicationHandler(DesktopCapturer::SourceId sourceId);
+
+ // Returns the full-screen window in place of the original window if all the
+ // criteria are met, or 0 if no such window found.
+ virtual DesktopCapturer::SourceId FindFullScreenWindow(
+ const DesktopCapturer::SourceList& window_list,
+ int64_t timestamp) const;
+
+ // Returns source id of original window associated with
+ // FullScreenApplicationHandler
+ DesktopCapturer::SourceId GetSourceId() const;
+
+ private:
+ const DesktopCapturer::SourceId source_id_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_FULL_SCREEN_APPLICATION_HANDLER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.cc b/third_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.cc
new file mode 100644
index 0000000000..d0bc9c7ca6
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.cc
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/full_screen_window_detector.h"
+#include "modules/desktop_capture/full_screen_application_handler.h"
+#include "rtc_base/time_utils.h"
+
+namespace webrtc {
+
+FullScreenWindowDetector::FullScreenWindowDetector(
+ ApplicationHandlerFactory application_handler_factory)
+ : application_handler_factory_(application_handler_factory),
+ last_update_time_ms_(0),
+ previous_source_id_(0),
+ no_handler_source_id_(0) {}
+
+DesktopCapturer::SourceId FullScreenWindowDetector::FindFullScreenWindow(
+ DesktopCapturer::SourceId original_source_id) {
+ if (app_handler_ == nullptr ||
+ app_handler_->GetSourceId() != original_source_id) {
+ return 0;
+ }
+ return app_handler_->FindFullScreenWindow(window_list_, last_update_time_ms_);
+}
+
+void FullScreenWindowDetector::UpdateWindowListIfNeeded(
+ DesktopCapturer::SourceId original_source_id,
+ rtc::FunctionView<bool(DesktopCapturer::SourceList*)> get_sources) {
+ const bool skip_update = previous_source_id_ != original_source_id;
+ previous_source_id_ = original_source_id;
+
+ // Here is an attempt to avoid redundant creating application handler in case
+ // when an instance of WindowCapturer is used to generate a thumbnail to show
+ // in picker by calling SelectSource and CaptureFrame for every available
+ // source.
+ if (skip_update) {
+ return;
+ }
+
+ CreateApplicationHandlerIfNeeded(original_source_id);
+ if (app_handler_ == nullptr) {
+ // There is no FullScreenApplicationHandler specific for
+ // current application
+ return;
+ }
+
+ constexpr int64_t kUpdateIntervalMs = 500;
+
+ if ((rtc::TimeMillis() - last_update_time_ms_) <= kUpdateIntervalMs) {
+ return;
+ }
+
+ DesktopCapturer::SourceList window_list;
+ if (get_sources(&window_list)) {
+ last_update_time_ms_ = rtc::TimeMillis();
+ window_list_.swap(window_list);
+ }
+}
+
+void FullScreenWindowDetector::CreateApplicationHandlerIfNeeded(
+ DesktopCapturer::SourceId source_id) {
+ if (no_handler_source_id_ == source_id) {
+ return;
+ }
+
+ if (app_handler_ == nullptr || app_handler_->GetSourceId() != source_id) {
+ app_handler_ = application_handler_factory_
+ ? application_handler_factory_(source_id)
+ : nullptr;
+ }
+
+ if (app_handler_ == nullptr) {
+ no_handler_source_id_ = source_id;
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.h b/third_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.h
new file mode 100644
index 0000000000..998b720d90
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/full_screen_window_detector.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_FULL_SCREEN_WINDOW_DETECTOR_H_
+#define MODULES_DESKTOP_CAPTURE_FULL_SCREEN_WINDOW_DETECTOR_H_
+
+#include <memory>
+
+#include "api/function_view.h"
+#include "api/ref_counted_base.h"
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/full_screen_application_handler.h"
+
+namespace webrtc {
+
+// This is a way to handle switch to full-screen mode for application in some
+// specific cases:
+// - Chrome on MacOS creates a new window in full-screen mode to
+// show a tab full-screen and minimizes the old window.
+// - PowerPoint creates new windows in full-screen mode when user goes to
+// presentation mode (Slide Show Window, Presentation Window).
+//
+// To continue capturing in these cases, we try to find the new full-screen
+// window using criteria provided by application specific
+// FullScreenApplicationHandler.
+
+class FullScreenWindowDetector
+ : public rtc::RefCountedNonVirtual<FullScreenWindowDetector> {
+ public:
+ using ApplicationHandlerFactory =
+ std::function<std::unique_ptr<FullScreenApplicationHandler>(
+ DesktopCapturer::SourceId sourceId)>;
+
+ FullScreenWindowDetector(
+ ApplicationHandlerFactory application_handler_factory);
+
+ FullScreenWindowDetector(const FullScreenWindowDetector&) = delete;
+ FullScreenWindowDetector& operator=(const FullScreenWindowDetector&) = delete;
+
+ // Returns the full-screen window in place of the original window if all the
+ // criteria provided by FullScreenApplicationHandler are met, or 0 if no such
+ // window found.
+ DesktopCapturer::SourceId FindFullScreenWindow(
+ DesktopCapturer::SourceId original_source_id);
+
+ // The caller should call this function periodically, implementation will
+ // update internal state no often than twice per second
+ void UpdateWindowListIfNeeded(
+ DesktopCapturer::SourceId original_source_id,
+ rtc::FunctionView<bool(DesktopCapturer::SourceList*)> get_sources);
+
+ static rtc::scoped_refptr<FullScreenWindowDetector>
+ CreateFullScreenWindowDetector();
+
+ protected:
+ std::unique_ptr<FullScreenApplicationHandler> app_handler_;
+
+ private:
+ void CreateApplicationHandlerIfNeeded(DesktopCapturer::SourceId source_id);
+
+ ApplicationHandlerFactory application_handler_factory_;
+
+ int64_t last_update_time_ms_;
+ DesktopCapturer::SourceId previous_source_id_;
+
+ // Save the source id when we fail to create an instance of
+ // CreateApplicationHandlerIfNeeded to avoid redundant attempt to do it again.
+ DesktopCapturer::SourceId no_handler_source_id_;
+
+ DesktopCapturer::SourceList window_list_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_FULL_SCREEN_WINDOW_DETECTOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc
new file mode 100644
index 0000000000..e9158bf0cc
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/base_capturer_pipewire.cc
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/wayland/base_capturer_pipewire.h"
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/linux/wayland/restore_token_manager.h"
+#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/random.h"
+#include "rtc_base/time_utils.h"
+
+namespace webrtc {
+
+namespace {
+
+using xdg_portal::RequestResponse;
+using xdg_portal::ScreenCapturePortalInterface;
+using xdg_portal::SessionDetails;
+
+} // namespace
+
+BaseCapturerPipeWire::BaseCapturerPipeWire(const DesktopCaptureOptions& options)
+ : BaseCapturerPipeWire(
+ options,
+ std::make_unique<ScreenCastPortal>(
+ ScreenCastPortal::CaptureSourceType::kAnyScreenContent,
+ this)) {
+ is_screencast_portal_ = true;
+}
+
+BaseCapturerPipeWire::BaseCapturerPipeWire(
+ const DesktopCaptureOptions& options,
+ std::unique_ptr<ScreenCapturePortalInterface> portal)
+ : options_(options),
+ is_screencast_portal_(false),
+ portal_(std::move(portal)) {
+ Random random(rtc::TimeMicros());
+ source_id_ = static_cast<SourceId>(random.Rand(1, INT_MAX));
+}
+
+BaseCapturerPipeWire::~BaseCapturerPipeWire() {}
+
+void BaseCapturerPipeWire::OnScreenCastRequestResult(RequestResponse result,
+ uint32_t stream_node_id,
+ int fd) {
+ if (result != RequestResponse::kSuccess ||
+ !options_.screencast_stream()->StartScreenCastStream(
+ stream_node_id, fd, options_.get_width(), options_.get_height())) {
+ capturer_failed_ = true;
+ RTC_LOG(LS_ERROR) << "ScreenCastPortal failed: "
+ << static_cast<uint>(result);
+ } else if (ScreenCastPortal* screencast_portal = GetScreenCastPortal()) {
+ if (!screencast_portal->RestoreToken().empty()) {
+ RestoreTokenManager::GetInstance().AddToken(
+ source_id_, screencast_portal->RestoreToken());
+ }
+ }
+}
+
+void BaseCapturerPipeWire::OnScreenCastSessionClosed() {
+ if (!capturer_failed_) {
+ options_.screencast_stream()->StopScreenCastStream();
+ }
+}
+
+void BaseCapturerPipeWire::UpdateResolution(uint32_t width, uint32_t height) {
+ if (!capturer_failed_) {
+ options_.screencast_stream()->UpdateScreenCastStreamResolution(width,
+ height);
+ }
+}
+
+void BaseCapturerPipeWire::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+
+ callback_ = callback;
+
+ if (ScreenCastPortal* screencast_portal = GetScreenCastPortal()) {
+ screencast_portal->SetPersistMode(
+ ScreenCastPortal::PersistMode::kTransient);
+ if (selected_source_id_) {
+ screencast_portal->SetRestoreToken(
+ RestoreTokenManager::GetInstance().TakeToken(selected_source_id_));
+ }
+ }
+
+ portal_->Start();
+}
+
+void BaseCapturerPipeWire::CaptureFrame() {
+ if (capturer_failed_) {
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+
+ std::unique_ptr<DesktopFrame> frame =
+ options_.screencast_stream()->CaptureFrame();
+
+ if (!frame || !frame->data()) {
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+
+ // TODO(julien.isorce): http://crbug.com/945468. Set the icc profile on
+ // the frame, see ScreenCapturerX11::CaptureFrame.
+
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+}
+
+// Keep in sync with defines at browser/actors/WebRTCParent.jsm
+// With PipeWire we can't select which system resource is shared so
+// we don't create a window/screen list. Instead we place these constants
+// as window name/id so frontend code can identify PipeWire backend
+// and does not try to create screen/window preview.
+
+#define PIPEWIRE_ID 0xaffffff
+#define PIPEWIRE_NAME "####_PIPEWIRE_PORTAL_####"
+
+bool BaseCapturerPipeWire::GetSourceList(SourceList* sources) {
+ RTC_DCHECK(sources->size() == 0);
+ // List of available screens is already presented by the xdg-desktop-portal,
+ // so we just need a (valid) source id for any callers to pass around, even
+ // though it doesn't mean anything to us. Until the user selects a source in
+ // xdg-desktop-portal we'll just end up returning empty frames. Note that "0"
+ // is often treated as a null/placeholder id, so we shouldn't use that.
+ // TODO(https://crbug.com/1297671): Reconsider type of ID when plumbing
+ // token that will enable stream re-use.
+ sources->push_back({source_id_});
+ return true;
+}
+
+bool BaseCapturerPipeWire::SelectSource(SourceId id) {
+ // Screen selection is handled by the xdg-desktop-portal.
+ selected_source_id_ = id;
+ return id == PIPEWIRE_ID;
+}
+
+SessionDetails BaseCapturerPipeWire::GetSessionDetails() {
+ return portal_->GetSessionDetails();
+}
+
+ScreenCastPortal* BaseCapturerPipeWire::GetScreenCastPortal() {
+ return is_screencast_portal_ ? static_cast<ScreenCastPortal*>(portal_.get())
+ : nullptr;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h
new file mode 100644
index 0000000000..6e18f4d11b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/base_capturer_pipewire.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_BASE_CAPTURER_PIPEWIRE_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_BASE_CAPTURER_PIPEWIRE_H_
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/linux/wayland/portal_request_response.h"
+#include "modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h"
+#include "modules/desktop_capture/linux/wayland/screencast_portal.h"
+#include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h"
+#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
+#include "modules/desktop_capture/linux/wayland/xdg_session_details.h"
+
+namespace webrtc {
+
+class BaseCapturerPipeWire : public DesktopCapturer,
+ public ScreenCastPortal::PortalNotifier {
+ public:
+ explicit BaseCapturerPipeWire(const DesktopCaptureOptions& options);
+ BaseCapturerPipeWire(
+ const DesktopCaptureOptions& options,
+ std::unique_ptr<xdg_portal::ScreenCapturePortalInterface> portal);
+ ~BaseCapturerPipeWire() override;
+
+ BaseCapturerPipeWire(const BaseCapturerPipeWire&) = delete;
+ BaseCapturerPipeWire& operator=(const BaseCapturerPipeWire&) = delete;
+
+ // DesktopCapturer interface.
+ void Start(Callback* delegate) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+
+ // ScreenCastPortal::PortalNotifier interface.
+ void OnScreenCastRequestResult(xdg_portal::RequestResponse result,
+ uint32_t stream_node_id,
+ int fd) override;
+ void OnScreenCastSessionClosed() override;
+ void UpdateResolution(uint32_t width, uint32_t height) override;
+
+ xdg_portal::SessionDetails GetSessionDetails();
+
+ private:
+ ScreenCastPortal* GetScreenCastPortal();
+
+ DesktopCaptureOptions options_ = {};
+ Callback* callback_ = nullptr;
+ bool capturer_failed_ = false;
+ bool is_screencast_portal_ = false;
+
+ // SourceId that is selected using SelectSource() and that we previously
+ // returned in GetSourceList(). This should be a SourceId that has a restore
+ // token associated with it and can be restored if we have required version
+ // of xdg-desktop-portal.
+ SourceId selected_source_id_ = 0;
+ // SourceID we randomly generate and that is returned in GetSourceList() as
+ // available source that will later get assigned to a restore token in order
+ // to be restored later using SelectSource().
+ SourceId source_id_ = 0;
+ std::unique_ptr<xdg_portal::ScreenCapturePortalInterface> portal_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_BASE_CAPTURER_PIPEWIRE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/drm.sigs b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/drm.sigs
new file mode 100644
index 0000000000..226979fe16
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/drm.sigs
@@ -0,0 +1,11 @@
+// Copyright 2021 The WebRTC project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//------------------------------------------------
+// Functions from DRM used in capturer code.
+//--------
+
+// xf86drm.h
+int drmGetDevices2(uint32_t flags, drmDevicePtr devices[], int max_devices);
+void drmFreeDevices(drmDevicePtr devices[], int count);
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/egl_dmabuf.cc b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/egl_dmabuf.cc
new file mode 100644
index 0000000000..5bbd5d7aba
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/egl_dmabuf.cc
@@ -0,0 +1,703 @@
+/*
+ * Copyright 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/wayland/egl_dmabuf.h"
+
+#include <asm/ioctl.h>
+#include <dlfcn.h>
+#include <fcntl.h>
+#include <libdrm/drm_fourcc.h>
+#include <linux/types.h>
+#include <spa/param/video/format-utils.h>
+#include <unistd.h>
+#include <xf86drm.h>
+
+#include "absl/memory/memory.h"
+#include "absl/types/optional.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/sanitizer.h"
+#include "rtc_base/string_encode.h"
+
+namespace webrtc {
+
+// EGL
+typedef EGLBoolean (*eglBindAPI_func)(EGLenum api);
+typedef EGLContext (*eglCreateContext_func)(EGLDisplay dpy,
+ EGLConfig config,
+ EGLContext share_context,
+ const EGLint* attrib_list);
+typedef EGLBoolean (*eglDestroyContext_func)(EGLDisplay display,
+ EGLContext context);
+typedef EGLBoolean (*eglTerminate_func)(EGLDisplay display);
+typedef EGLImageKHR (*eglCreateImageKHR_func)(EGLDisplay dpy,
+ EGLContext ctx,
+ EGLenum target,
+ EGLClientBuffer buffer,
+ const EGLint* attrib_list);
+typedef EGLBoolean (*eglDestroyImageKHR_func)(EGLDisplay dpy,
+ EGLImageKHR image);
+typedef EGLint (*eglGetError_func)(void);
+typedef void* (*eglGetProcAddress_func)(const char*);
+typedef EGLDisplay (*eglGetPlatformDisplayEXT_func)(EGLenum platform,
+ void* native_display,
+ const EGLint* attrib_list);
+typedef EGLDisplay (*eglGetPlatformDisplay_func)(EGLenum platform,
+ void* native_display,
+ const EGLAttrib* attrib_list);
+
+typedef EGLBoolean (*eglInitialize_func)(EGLDisplay dpy,
+ EGLint* major,
+ EGLint* minor);
+typedef EGLBoolean (*eglMakeCurrent_func)(EGLDisplay dpy,
+ EGLSurface draw,
+ EGLSurface read,
+ EGLContext ctx);
+typedef EGLBoolean (*eglQueryDmaBufFormatsEXT_func)(EGLDisplay dpy,
+ EGLint max_formats,
+ EGLint* formats,
+ EGLint* num_formats);
+typedef EGLBoolean (*eglQueryDmaBufModifiersEXT_func)(EGLDisplay dpy,
+ EGLint format,
+ EGLint max_modifiers,
+ EGLuint64KHR* modifiers,
+ EGLBoolean* external_only,
+ EGLint* num_modifiers);
+typedef const char* (*eglQueryString_func)(EGLDisplay dpy, EGLint name);
+typedef void (*glEGLImageTargetTexture2DOES_func)(GLenum target,
+ GLeglImageOES image);
+
+// This doesn't follow naming conventions in WebRTC, where the naming
+// should look like e.g. egl_bind_api instead of EglBindAPI, however
+// we named them according to the exported functions they map to for
+// consistency.
+eglBindAPI_func EglBindAPI = nullptr;
+eglCreateContext_func EglCreateContext = nullptr;
+eglDestroyContext_func EglDestroyContext = nullptr;
+eglTerminate_func EglTerminate = nullptr;
+eglCreateImageKHR_func EglCreateImageKHR = nullptr;
+eglDestroyImageKHR_func EglDestroyImageKHR = nullptr;
+eglGetError_func EglGetError = nullptr;
+eglGetProcAddress_func EglGetProcAddress = nullptr;
+eglGetPlatformDisplayEXT_func EglGetPlatformDisplayEXT = nullptr;
+eglGetPlatformDisplay_func EglGetPlatformDisplay = nullptr;
+eglInitialize_func EglInitialize = nullptr;
+eglMakeCurrent_func EglMakeCurrent = nullptr;
+eglQueryDmaBufFormatsEXT_func EglQueryDmaBufFormatsEXT = nullptr;
+eglQueryDmaBufModifiersEXT_func EglQueryDmaBufModifiersEXT = nullptr;
+eglQueryString_func EglQueryString = nullptr;
+glEGLImageTargetTexture2DOES_func GlEGLImageTargetTexture2DOES = nullptr;
+
+// GL
+typedef void (*glBindTexture_func)(GLenum target, GLuint texture);
+typedef void (*glDeleteTextures_func)(GLsizei n, const GLuint* textures);
+typedef void (*glGenTextures_func)(GLsizei n, GLuint* textures);
+typedef GLenum (*glGetError_func)(void);
+typedef const GLubyte* (*glGetString_func)(GLenum name);
+typedef void (*glGetTexImage_func)(GLenum target,
+ GLint level,
+ GLenum format,
+ GLenum type,
+ void* pixels);
+typedef void (*glTexParameteri_func)(GLenum target, GLenum pname, GLint param);
+typedef void* (*glXGetProcAddressARB_func)(const char*);
+
+// This doesn't follow naming conventions in WebRTC, where the naming
+// should look like e.g. egl_bind_api instead of EglBindAPI, however
+// we named them according to the exported functions they map to for
+// consistency.
+glBindTexture_func GlBindTexture = nullptr;
+glDeleteTextures_func GlDeleteTextures = nullptr;
+glGenTextures_func GlGenTextures = nullptr;
+glGetError_func GlGetError = nullptr;
+glGetString_func GlGetString = nullptr;
+glGetTexImage_func GlGetTexImage = nullptr;
+glTexParameteri_func GlTexParameteri = nullptr;
+glXGetProcAddressARB_func GlXGetProcAddressARB = nullptr;
+
+static const std::string FormatGLError(GLenum err) {
+ switch (err) {
+ case GL_NO_ERROR:
+ return "GL_NO_ERROR";
+ case GL_INVALID_ENUM:
+ return "GL_INVALID_ENUM";
+ case GL_INVALID_VALUE:
+ return "GL_INVALID_VALUE";
+ case GL_INVALID_OPERATION:
+ return "GL_INVALID_OPERATION";
+ case GL_STACK_OVERFLOW:
+ return "GL_STACK_OVERFLOW";
+ case GL_STACK_UNDERFLOW:
+ return "GL_STACK_UNDERFLOW";
+ case GL_OUT_OF_MEMORY:
+ return "GL_OUT_OF_MEMORY";
+ default:
+ return "GL error code: " + std::to_string(err);
+ }
+}
+
+static const std::string FormatEGLError(EGLint err) {
+ switch (err) {
+ case EGL_NOT_INITIALIZED:
+ return "EGL_NOT_INITIALIZED";
+ case EGL_BAD_ACCESS:
+ return "EGL_BAD_ACCESS";
+ case EGL_BAD_ALLOC:
+ return "EGL_BAD_ALLOC";
+ case EGL_BAD_ATTRIBUTE:
+ return "EGL_BAD_ATTRIBUTE";
+ case EGL_BAD_CONTEXT:
+ return "EGL_BAD_CONTEXT";
+ case EGL_BAD_CONFIG:
+ return "EGL_BAD_CONFIG";
+ case EGL_BAD_CURRENT_SURFACE:
+ return "EGL_BAD_CURRENT_SURFACE";
+ case EGL_BAD_DISPLAY:
+ return "EGL_BAD_DISPLAY";
+ case EGL_BAD_SURFACE:
+ return "EGL_BAD_SURFACE";
+ case EGL_BAD_MATCH:
+ return "EGL_BAD_MATCH";
+ case EGL_BAD_PARAMETER:
+ return "EGL_BAD_PARAMETER";
+ case EGL_BAD_NATIVE_PIXMAP:
+ return "EGL_BAD_NATIVE_PIXMAP";
+ case EGL_BAD_NATIVE_WINDOW:
+ return "EGL_BAD_NATIVE_WINDOW";
+ case EGL_CONTEXT_LOST:
+ return "EGL_CONTEXT_LOST";
+ default:
+ return "EGL error code: " + std::to_string(err);
+ }
+}
+
+static uint32_t SpaPixelFormatToDrmFormat(uint32_t spa_format) {
+ switch (spa_format) {
+ case SPA_VIDEO_FORMAT_RGBA:
+ return DRM_FORMAT_ABGR8888;
+ case SPA_VIDEO_FORMAT_RGBx:
+ return DRM_FORMAT_XBGR8888;
+ case SPA_VIDEO_FORMAT_BGRA:
+ return DRM_FORMAT_ARGB8888;
+ case SPA_VIDEO_FORMAT_BGRx:
+ return DRM_FORMAT_XRGB8888;
+ default:
+ return DRM_FORMAT_INVALID;
+ }
+}
+
+static void CloseLibrary(void* library) {
+ if (library) {
+ dlclose(library);
+ library = nullptr;
+ }
+}
+
+static void* g_lib_egl = nullptr;
+
+RTC_NO_SANITIZE("cfi-icall")
+static bool OpenEGL() {
+ g_lib_egl = dlopen("libEGL.so.1", RTLD_NOW | RTLD_GLOBAL);
+ if (g_lib_egl) {
+ EglGetProcAddress =
+ (eglGetProcAddress_func)dlsym(g_lib_egl, "eglGetProcAddress");
+ return EglGetProcAddress;
+ }
+
+ return false;
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+static bool LoadEGL() {
+ if (OpenEGL()) {
+ EglBindAPI = (eglBindAPI_func)EglGetProcAddress("eglBindAPI");
+ EglCreateContext =
+ (eglCreateContext_func)EglGetProcAddress("eglCreateContext");
+ EglDestroyContext =
+ (eglDestroyContext_func)EglGetProcAddress("eglDestroyContext");
+ EglTerminate = (eglTerminate_func)EglGetProcAddress("eglTerminate");
+ EglCreateImageKHR =
+ (eglCreateImageKHR_func)EglGetProcAddress("eglCreateImageKHR");
+ EglDestroyImageKHR =
+ (eglDestroyImageKHR_func)EglGetProcAddress("eglDestroyImageKHR");
+ EglGetError = (eglGetError_func)EglGetProcAddress("eglGetError");
+ EglGetPlatformDisplayEXT = (eglGetPlatformDisplayEXT_func)EglGetProcAddress(
+ "eglGetPlatformDisplayEXT");
+ EglGetPlatformDisplay =
+ (eglGetPlatformDisplay_func)EglGetProcAddress("eglGetPlatformDisplay");
+ EglInitialize = (eglInitialize_func)EglGetProcAddress("eglInitialize");
+ EglMakeCurrent = (eglMakeCurrent_func)EglGetProcAddress("eglMakeCurrent");
+ EglQueryString = (eglQueryString_func)EglGetProcAddress("eglQueryString");
+ GlEGLImageTargetTexture2DOES =
+ (glEGLImageTargetTexture2DOES_func)EglGetProcAddress(
+ "glEGLImageTargetTexture2DOES");
+
+ return EglBindAPI && EglCreateContext && EglCreateImageKHR &&
+ EglTerminate && EglDestroyContext && EglDestroyImageKHR &&
+ EglGetError && EglGetPlatformDisplayEXT && EglGetPlatformDisplay &&
+ EglInitialize && EglMakeCurrent && EglQueryString &&
+ GlEGLImageTargetTexture2DOES;
+ }
+
+ return false;
+}
+
+static void* g_lib_gl = nullptr;
+
+RTC_NO_SANITIZE("cfi-icall")
+static bool OpenGL() {
+ std::vector<std::string> names = {"libGL.so.1", "libGL.so"};
+ for (const std::string& name : names) {
+ g_lib_gl = dlopen(name.c_str(), RTLD_NOW | RTLD_GLOBAL);
+ if (g_lib_gl) {
+ GlXGetProcAddressARB =
+ (glXGetProcAddressARB_func)dlsym(g_lib_gl, "glXGetProcAddressARB");
+ return GlXGetProcAddressARB;
+ }
+ }
+
+ return false;
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+static bool LoadGL() {
+ if (OpenGL()) {
+ GlGetString = (glGetString_func)GlXGetProcAddressARB("glGetString");
+ if (!GlGetString) {
+ return false;
+ }
+
+ GlBindTexture = (glBindTexture_func)GlXGetProcAddressARB("glBindTexture");
+ GlDeleteTextures =
+ (glDeleteTextures_func)GlXGetProcAddressARB("glDeleteTextures");
+ GlGenTextures = (glGenTextures_func)GlXGetProcAddressARB("glGenTextures");
+ GlGetError = (glGetError_func)GlXGetProcAddressARB("glGetError");
+ GlGetTexImage = (glGetTexImage_func)GlXGetProcAddressARB("glGetTexImage");
+ GlTexParameteri =
+ (glTexParameteri_func)GlXGetProcAddressARB("glTexParameteri");
+
+ return GlBindTexture && GlDeleteTextures && GlGenTextures && GlGetError &&
+ GlGetTexImage && GlTexParameteri;
+ }
+
+ return false;
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+EglDmaBuf::EglDmaBuf() {
+ if (!LoadEGL()) {
+ RTC_LOG(LS_ERROR) << "Unable to load EGL entry functions.";
+ CloseLibrary(g_lib_egl);
+ return;
+ }
+
+ if (!LoadGL()) {
+ RTC_LOG(LS_ERROR) << "Failed to load OpenGL entry functions.";
+ CloseLibrary(g_lib_gl);
+ return;
+ }
+
+ if (!GetClientExtensions(EGL_NO_DISPLAY, EGL_EXTENSIONS)) {
+ return;
+ }
+
+ bool has_platform_base_ext = false;
+ bool has_platform_gbm_ext = false;
+ bool has_khr_platform_gbm_ext = false;
+
+ for (const auto& extension : egl_.extensions) {
+ if (extension == "EGL_EXT_platform_base") {
+ has_platform_base_ext = true;
+ continue;
+ } else if (extension == "EGL_MESA_platform_gbm") {
+ has_platform_gbm_ext = true;
+ continue;
+ } else if (extension == "EGL_KHR_platform_gbm") {
+ has_khr_platform_gbm_ext = true;
+ continue;
+ }
+ }
+
+ if (!has_platform_base_ext || !has_platform_gbm_ext ||
+ !has_khr_platform_gbm_ext) {
+ RTC_LOG(LS_ERROR) << "One of required EGL extensions is missing";
+ return;
+ }
+
+ egl_.display = EglGetPlatformDisplay(EGL_PLATFORM_WAYLAND_KHR,
+ (void*)EGL_DEFAULT_DISPLAY, nullptr);
+
+ if (egl_.display == EGL_NO_DISPLAY) {
+ RTC_LOG(LS_ERROR) << "Failed to obtain default EGL display: "
+ << FormatEGLError(EglGetError()) << "\n"
+ << "Defaulting to using first available render node";
+ absl::optional<std::string> render_node = GetRenderNode();
+ if (!render_node) {
+ return;
+ }
+
+ drm_fd_ = open(render_node->c_str(), O_RDWR);
+
+ if (drm_fd_ < 0) {
+ RTC_LOG(LS_ERROR) << "Failed to open drm render node: "
+ << strerror(errno);
+ return;
+ }
+
+ gbm_device_ = gbm_create_device(drm_fd_);
+
+ if (!gbm_device_) {
+ RTC_LOG(LS_ERROR) << "Cannot create GBM device: " << strerror(errno);
+ close(drm_fd_);
+ return;
+ }
+
+ // Use eglGetPlatformDisplayEXT() to get the display pointer
+ // if the implementation supports it.
+ egl_.display =
+ EglGetPlatformDisplayEXT(EGL_PLATFORM_GBM_KHR, gbm_device_, nullptr);
+ }
+
+ if (egl_.display == EGL_NO_DISPLAY) {
+ RTC_LOG(LS_ERROR) << "Error during obtaining EGL display: "
+ << FormatEGLError(EglGetError());
+ return;
+ }
+
+ EGLint major, minor;
+ if (EglInitialize(egl_.display, &major, &minor) == EGL_FALSE) {
+ RTC_LOG(LS_ERROR) << "Error during eglInitialize: "
+ << FormatEGLError(EglGetError());
+ return;
+ }
+
+ if (EglBindAPI(EGL_OPENGL_API) == EGL_FALSE) {
+ RTC_LOG(LS_ERROR) << "bind OpenGL API failed";
+ return;
+ }
+
+ egl_.context =
+ EglCreateContext(egl_.display, nullptr, EGL_NO_CONTEXT, nullptr);
+
+ if (egl_.context == EGL_NO_CONTEXT) {
+ RTC_LOG(LS_ERROR) << "Couldn't create EGL context: "
+ << FormatGLError(EglGetError());
+ return;
+ }
+
+ if (!GetClientExtensions(egl_.display, EGL_EXTENSIONS)) {
+ return;
+ }
+
+ bool has_image_dma_buf_import_modifiers_ext = false;
+
+ for (const auto& extension : egl_.extensions) {
+ if (extension == "EGL_EXT_image_dma_buf_import") {
+ has_image_dma_buf_import_ext_ = true;
+ continue;
+ } else if (extension == "EGL_EXT_image_dma_buf_import_modifiers") {
+ has_image_dma_buf_import_modifiers_ext = true;
+ continue;
+ }
+ }
+
+ if (has_image_dma_buf_import_ext_ && has_image_dma_buf_import_modifiers_ext) {
+ EglQueryDmaBufFormatsEXT = (eglQueryDmaBufFormatsEXT_func)EglGetProcAddress(
+ "eglQueryDmaBufFormatsEXT");
+ EglQueryDmaBufModifiersEXT =
+ (eglQueryDmaBufModifiersEXT_func)EglGetProcAddress(
+ "eglQueryDmaBufModifiersEXT");
+ }
+
+ RTC_LOG(LS_INFO) << "Egl initialization succeeded";
+ egl_initialized_ = true;
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+EglDmaBuf::~EglDmaBuf() {
+ if (gbm_device_) {
+ gbm_device_destroy(gbm_device_);
+ close(drm_fd_);
+ }
+
+ if (egl_.context != EGL_NO_CONTEXT) {
+ EglDestroyContext(egl_.display, egl_.context);
+ }
+
+ if (egl_.display != EGL_NO_DISPLAY) {
+ EglTerminate(egl_.display);
+ }
+
+ // BUG: crbug.com/1290566
+ // Closing libEGL.so.1 when using NVidia drivers causes a crash
+ // when EglGetPlatformDisplayEXT() is used, at least this one is enough
+ // to be called to make it crash.
+ // It also looks that libepoxy and glad don't dlclose it either
+ // CloseLibrary(g_lib_egl);
+ // CloseLibrary(g_lib_gl);
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+bool EglDmaBuf::GetClientExtensions(EGLDisplay dpy, EGLint name) {
+ // Get the list of client extensions
+ const char* client_extensions_cstring = EglQueryString(dpy, name);
+ if (!client_extensions_cstring) {
+ // If eglQueryString() returned NULL, the implementation doesn't support
+ // EGL_EXT_client_extensions. Expect an EGL_BAD_DISPLAY error.
+ RTC_LOG(LS_ERROR) << "No client extensions defined! "
+ << FormatEGLError(EglGetError());
+ return false;
+ }
+
+ std::vector<absl::string_view> client_extensions =
+ rtc::split(client_extensions_cstring, ' ');
+ for (const auto& extension : client_extensions) {
+ egl_.extensions.push_back(std::string(extension));
+ }
+
+ return true;
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+std::unique_ptr<uint8_t[]> EglDmaBuf::ImageFromDmaBuf(
+ const DesktopSize& size,
+ uint32_t format,
+ const std::vector<PlaneData>& plane_datas,
+ uint64_t modifier) {
+ std::unique_ptr<uint8_t[]> src;
+
+ if (!egl_initialized_) {
+ return src;
+ }
+
+ if (plane_datas.size() <= 0) {
+ RTC_LOG(LS_ERROR) << "Failed to process buffer: invalid number of planes";
+ return src;
+ }
+
+ EGLint attribs[47];
+ int atti = 0;
+
+ attribs[atti++] = EGL_WIDTH;
+ attribs[atti++] = static_cast<EGLint>(size.width());
+ attribs[atti++] = EGL_HEIGHT;
+ attribs[atti++] = static_cast<EGLint>(size.height());
+ attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT;
+ attribs[atti++] = SpaPixelFormatToDrmFormat(format);
+
+ if (plane_datas.size() > 0) {
+ attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT;
+ attribs[atti++] = plane_datas[0].fd;
+ attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT;
+ attribs[atti++] = plane_datas[0].offset;
+ attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT;
+ attribs[atti++] = plane_datas[0].stride;
+
+ if (modifier != DRM_FORMAT_MOD_INVALID) {
+ attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT;
+ attribs[atti++] = modifier & 0xFFFFFFFF;
+ attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT;
+ attribs[atti++] = modifier >> 32;
+ }
+ }
+
+ if (plane_datas.size() > 1) {
+ attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT;
+ attribs[atti++] = plane_datas[1].fd;
+ attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT;
+ attribs[atti++] = plane_datas[1].offset;
+ attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT;
+ attribs[atti++] = plane_datas[1].stride;
+
+ if (modifier != DRM_FORMAT_MOD_INVALID) {
+ attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT;
+ attribs[atti++] = modifier & 0xFFFFFFFF;
+ attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT;
+ attribs[atti++] = modifier >> 32;
+ }
+ }
+
+ if (plane_datas.size() > 2) {
+ attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT;
+ attribs[atti++] = plane_datas[2].fd;
+ attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT;
+ attribs[atti++] = plane_datas[2].offset;
+ attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT;
+ attribs[atti++] = plane_datas[2].stride;
+
+ if (modifier != DRM_FORMAT_MOD_INVALID) {
+ attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT;
+ attribs[atti++] = modifier & 0xFFFFFFFF;
+ attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT;
+ attribs[atti++] = modifier >> 32;
+ }
+ }
+
+ if (plane_datas.size() > 3) {
+ attribs[atti++] = EGL_DMA_BUF_PLANE3_FD_EXT;
+ attribs[atti++] = plane_datas[3].fd;
+ attribs[atti++] = EGL_DMA_BUF_PLANE3_OFFSET_EXT;
+ attribs[atti++] = plane_datas[3].offset;
+ attribs[atti++] = EGL_DMA_BUF_PLANE3_PITCH_EXT;
+ attribs[atti++] = plane_datas[3].stride;
+
+ if (modifier != DRM_FORMAT_MOD_INVALID) {
+ attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT;
+ attribs[atti++] = modifier & 0xFFFFFFFF;
+ attribs[atti++] = EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT;
+ attribs[atti++] = modifier >> 32;
+ }
+ }
+
+ attribs[atti++] = EGL_NONE;
+
+ // bind context to render thread
+ EglMakeCurrent(egl_.display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_.context);
+
+ // create EGL image from attribute list
+ EGLImageKHR image = EglCreateImageKHR(
+ egl_.display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, attribs);
+
+ if (image == EGL_NO_IMAGE) {
+ RTC_LOG(LS_ERROR) << "Failed to record frame: Error creating EGLImage - "
+ << FormatEGLError(EglGetError());
+ return src;
+ }
+
+ // create GL 2D texture for framebuffer
+ GLuint texture;
+ GlGenTextures(1, &texture);
+ GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ GlTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ GlBindTexture(GL_TEXTURE_2D, texture);
+ GlEGLImageTargetTexture2DOES(GL_TEXTURE_2D, image);
+
+ src = std::make_unique<uint8_t[]>(plane_datas[0].stride * size.height());
+
+ GLenum gl_format = GL_BGRA;
+ switch (format) {
+ case SPA_VIDEO_FORMAT_RGBx:
+ gl_format = GL_RGBA;
+ break;
+ case SPA_VIDEO_FORMAT_RGBA:
+ gl_format = GL_RGBA;
+ break;
+ case SPA_VIDEO_FORMAT_BGRx:
+ gl_format = GL_BGRA;
+ break;
+ default:
+ gl_format = GL_BGRA;
+ break;
+ }
+ GlGetTexImage(GL_TEXTURE_2D, 0, gl_format, GL_UNSIGNED_BYTE, src.get());
+
+ if (GlGetError()) {
+ RTC_LOG(LS_ERROR) << "Failed to get image from DMA buffer.";
+ return src;
+ }
+
+ GlDeleteTextures(1, &texture);
+ EglDestroyImageKHR(egl_.display, image);
+
+ return src;
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+std::vector<uint64_t> EglDmaBuf::QueryDmaBufModifiers(uint32_t format) {
+ if (!egl_initialized_) {
+ return {};
+ }
+
+ // Explicit modifiers not supported, return just DRM_FORMAT_MOD_INVALID as we
+ // can still use modifier-less DMA-BUFs if we have required extension
+ if (EglQueryDmaBufFormatsEXT == nullptr ||
+ EglQueryDmaBufModifiersEXT == nullptr) {
+ return has_image_dma_buf_import_ext_
+ ? std::vector<uint64_t>{DRM_FORMAT_MOD_INVALID}
+ : std::vector<uint64_t>{};
+ }
+
+ uint32_t drm_format = SpaPixelFormatToDrmFormat(format);
+ // Should never happen as it's us who controls the list of supported formats
+ RTC_DCHECK(drm_format != DRM_FORMAT_INVALID);
+
+ EGLint count = 0;
+ EGLBoolean success =
+ EglQueryDmaBufFormatsEXT(egl_.display, 0, nullptr, &count);
+
+ if (!success || !count) {
+ RTC_LOG(LS_WARNING) << "Cannot query the number of formats.";
+ return {DRM_FORMAT_MOD_INVALID};
+ }
+
+ std::vector<uint32_t> formats(count);
+ if (!EglQueryDmaBufFormatsEXT(egl_.display, count,
+ reinterpret_cast<EGLint*>(formats.data()),
+ &count)) {
+ RTC_LOG(LS_WARNING) << "Cannot query a list of formats.";
+ return {DRM_FORMAT_MOD_INVALID};
+ }
+
+ if (std::find(formats.begin(), formats.end(), drm_format) == formats.end()) {
+ RTC_LOG(LS_WARNING) << "Format " << drm_format
+ << " not supported for modifiers.";
+ return {DRM_FORMAT_MOD_INVALID};
+ }
+
+ success = EglQueryDmaBufModifiersEXT(egl_.display, drm_format, 0, nullptr,
+ nullptr, &count);
+
+ if (!success || !count) {
+ RTC_LOG(LS_WARNING) << "Cannot query the number of modifiers.";
+ return {DRM_FORMAT_MOD_INVALID};
+ }
+
+ std::vector<uint64_t> modifiers(count);
+ if (!EglQueryDmaBufModifiersEXT(egl_.display, drm_format, count,
+ modifiers.data(), nullptr, &count)) {
+ RTC_LOG(LS_WARNING) << "Cannot query a list of modifiers.";
+ }
+
+ // Support modifier-less buffers
+ modifiers.push_back(DRM_FORMAT_MOD_INVALID);
+ return modifiers;
+}
+
+absl::optional<std::string> EglDmaBuf::GetRenderNode() {
+ int max_devices = drmGetDevices2(0, nullptr, 0);
+ if (max_devices <= 0) {
+ RTC_LOG(LS_ERROR) << "drmGetDevices2() has not found any devices (errno="
+ << -max_devices << ")";
+ return absl::nullopt;
+ }
+
+ std::vector<drmDevicePtr> devices(max_devices);
+ int ret = drmGetDevices2(0, devices.data(), max_devices);
+ if (ret < 0) {
+ RTC_LOG(LS_ERROR) << "drmGetDevices2() returned an error " << ret;
+ return absl::nullopt;
+ }
+
+ std::string render_node;
+
+ for (const drmDevicePtr& device : devices) {
+ if (device->available_nodes & (1 << DRM_NODE_RENDER)) {
+ render_node = device->nodes[DRM_NODE_RENDER];
+ break;
+ }
+ }
+
+ drmFreeDevices(devices.data(), ret);
+ return render_node;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/egl_dmabuf.h b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/egl_dmabuf.h
new file mode 100644
index 0000000000..f1d96b2f80
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/egl_dmabuf.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_EGL_DMABUF_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_EGL_DMABUF_H_
+
+#include <epoxy/egl.h>
+#include <epoxy/gl.h>
+#include <gbm.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+class EglDmaBuf {
+ public:
+ struct EGLStruct {
+ std::vector<std::string> extensions;
+ EGLDisplay display = EGL_NO_DISPLAY;
+ EGLContext context = EGL_NO_CONTEXT;
+ };
+
+ struct PlaneData {
+ int32_t fd;
+ uint32_t stride;
+ uint32_t offset;
+ };
+
+ EglDmaBuf();
+ ~EglDmaBuf();
+
+ std::unique_ptr<uint8_t[]> ImageFromDmaBuf(
+ const DesktopSize& size,
+ uint32_t format,
+ const std::vector<PlaneData>& plane_datas,
+ uint64_t modifiers);
+ std::vector<uint64_t> QueryDmaBufModifiers(uint32_t format);
+
+ bool IsEglInitialized() const { return egl_initialized_; }
+
+ private:
+ bool GetClientExtensions(EGLDisplay dpy, EGLint name);
+
+ bool egl_initialized_ = false;
+ bool has_image_dma_buf_import_ext_ = false;
+ int32_t drm_fd_ = -1; // for GBM buffer mmap
+ gbm_device* gbm_device_ = nullptr; // for passed GBM buffer retrieval
+
+ EGLStruct egl_;
+
+ absl::optional<std::string> GetRenderNode();
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_EGL_DMABUF_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.cc b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.cc
new file mode 100644
index 0000000000..3d33b0fbb8
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h"
+
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+MouseCursorMonitorPipeWire::MouseCursorMonitorPipeWire(
+ const DesktopCaptureOptions& options)
+ : options_(options) {
+ sequence_checker_.Detach();
+}
+
+MouseCursorMonitorPipeWire::~MouseCursorMonitorPipeWire() {}
+
+void MouseCursorMonitorPipeWire::Init(Callback* callback, Mode mode) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+
+ callback_ = callback;
+ mode_ = mode;
+}
+
+void MouseCursorMonitorPipeWire::Capture() {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ RTC_DCHECK(callback_);
+
+ std::unique_ptr<MouseCursor> mouse_cursor =
+ options_.screencast_stream()->CaptureCursor();
+
+ if (mouse_cursor && mouse_cursor->image()->data()) {
+ callback_->OnMouseCursor(mouse_cursor.release());
+ }
+
+ if (mode_ == SHAPE_AND_POSITION) {
+ absl::optional<DesktopVector> mouse_cursor_position =
+ options_.screencast_stream()->CaptureCursorPosition();
+ if (mouse_cursor_position) {
+ callback_->OnMouseCursorPosition(mouse_cursor_position.value());
+ }
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h
new file mode 100644
index 0000000000..da670bece9
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_MOUSE_CURSOR_MONITOR_PIPEWIRE_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_MOUSE_CURSOR_MONITOR_PIPEWIRE_H_
+
+#include <memory>
+
+#include "api/scoped_refptr.h"
+#include "api/sequence_checker.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/mouse_cursor_monitor.h"
+#include "rtc_base/system/no_unique_address.h"
+
+namespace webrtc {
+
+class MouseCursorMonitorPipeWire : public MouseCursorMonitor {
+ public:
+ explicit MouseCursorMonitorPipeWire(const DesktopCaptureOptions& options);
+ ~MouseCursorMonitorPipeWire() override;
+
+ // MouseCursorMonitor:
+ void Init(Callback* callback, Mode mode) override;
+ void Capture() override;
+
+ DesktopCaptureOptions options_ RTC_GUARDED_BY(sequence_checker_);
+ Callback* callback_ RTC_GUARDED_BY(sequence_checker_) = nullptr;
+ Mode mode_ RTC_GUARDED_BY(sequence_checker_) = SHAPE_AND_POSITION;
+ RTC_NO_UNIQUE_ADDRESS SequenceChecker sequence_checker_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_MOUSE_CURSOR_MONITOR_PIPEWIRE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.cc b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.cc
new file mode 100644
index 0000000000..9bd7cec7ff
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.cc
@@ -0,0 +1,1081 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.h"
+
+#include <gio/gunixfdlist.h>
+#include <glib-object.h>
+#include <spa/param/format-utils.h>
+#include <spa/param/props.h>
+
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/syscall.h>
+
+#include <cstring>
+#include <memory>
+#include <utility>
+
+#include "absl/memory/memory.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+#if defined(WEBRTC_DLOPEN_PIPEWIRE)
+#include "modules/desktop_capture/linux/pipewire_stubs.h"
+using modules_desktop_capture_linux::InitializeStubs;
+using modules_desktop_capture_linux::kModulePipewire03;
+using modules_desktop_capture_linux::StubPathMap;
+#endif // defined(WEBRTC_DLOPEN_PIPEWIRE)
+
+namespace webrtc {
+
+const char kDesktopBusName[] = "org.freedesktop.portal.Desktop";
+const char kDesktopObjectPath[] = "/org/freedesktop/portal/desktop";
+const char kDesktopRequestObjectPath[] =
+ "/org/freedesktop/portal/desktop/request";
+const char kSessionInterfaceName[] = "org.freedesktop.portal.Session";
+const char kRequestInterfaceName[] = "org.freedesktop.portal.Request";
+const char kScreenCastInterfaceName[] = "org.freedesktop.portal.ScreenCast";
+
+const int kBytesPerPixel = 4;
+
+#if defined(WEBRTC_DLOPEN_PIPEWIRE)
+const char kPipeWireLib[] = "libpipewire-0.3.so.0";
+#endif
+
+// static
+struct dma_buf_sync {
+ uint64_t flags;
+};
+#define DMA_BUF_SYNC_READ (1 << 0)
+#define DMA_BUF_SYNC_START (0 << 2)
+#define DMA_BUF_SYNC_END (1 << 2)
+#define DMA_BUF_BASE 'b'
+#define DMA_BUF_IOCTL_SYNC _IOW(DMA_BUF_BASE, 0, struct dma_buf_sync)
+
+static void SyncDmaBuf(int fd, uint64_t start_or_end) {
+ struct dma_buf_sync sync = {0};
+
+ sync.flags = start_or_end | DMA_BUF_SYNC_READ;
+
+ while (true) {
+ int ret;
+ ret = ioctl(fd, DMA_BUF_IOCTL_SYNC, &sync);
+ if (ret == -1 && errno == EINTR) {
+ continue;
+ } else if (ret == -1) {
+ RTC_LOG(LS_ERROR) << "Failed to synchronize DMA buffer: "
+ << g_strerror(errno);
+ break;
+ } else {
+ break;
+ }
+ }
+}
+
+class ScopedBuf {
+ public:
+ ScopedBuf() {}
+ ScopedBuf(unsigned char* map, int map_size, bool is_dma_buf, int fd)
+ : map_(map), map_size_(map_size), is_dma_buf_(is_dma_buf), fd_(fd) {}
+ ~ScopedBuf() {
+ if (map_ != MAP_FAILED) {
+ if (is_dma_buf_) {
+ SyncDmaBuf(fd_, DMA_BUF_SYNC_END);
+ }
+ munmap(map_, map_size_);
+ }
+ }
+
+ operator bool() { return map_ != MAP_FAILED; }
+
+ void initialize(unsigned char* map, int map_size, bool is_dma_buf, int fd) {
+ map_ = map;
+ map_size_ = map_size;
+ is_dma_buf_ = is_dma_buf;
+ fd_ = fd;
+ }
+
+ unsigned char* get() { return map_; }
+
+ protected:
+ unsigned char* map_ = nullptr;
+ int map_size_;
+ bool is_dma_buf_;
+ int fd_;
+};
+
+template <class T>
+class Scoped {
+ public:
+ Scoped() {}
+ explicit Scoped(T* val) { ptr_ = val; }
+ ~Scoped() { RTC_DCHECK_NOTREACHED(); }
+
+ T* operator->() { return ptr_; }
+
+ bool operator!() { return ptr_ == nullptr; }
+
+ T* get() { return ptr_; }
+
+ T** receive() {
+ RTC_CHECK(!ptr_);
+ return &ptr_;
+ }
+
+ Scoped& operator=(T* val) {
+ ptr_ = val;
+ return *this;
+ }
+
+ protected:
+ T* ptr_ = nullptr;
+};
+
+template <>
+Scoped<GError>::~Scoped() {
+ if (ptr_) {
+ g_error_free(ptr_);
+ }
+}
+
+template <>
+Scoped<gchar>::~Scoped() {
+ if (ptr_) {
+ g_free(ptr_);
+ }
+}
+
+template <>
+Scoped<GVariant>::~Scoped() {
+ if (ptr_) {
+ g_variant_unref(ptr_);
+ }
+}
+
+template <>
+Scoped<GVariantIter>::~Scoped() {
+ if (ptr_) {
+ g_variant_iter_free(ptr_);
+ }
+}
+
+template <>
+Scoped<GDBusMessage>::~Scoped() {
+ if (ptr_) {
+ g_object_unref(ptr_);
+ }
+}
+
+template <>
+Scoped<GUnixFDList>::~Scoped() {
+ if (ptr_) {
+ g_object_unref(ptr_);
+ }
+}
+
+void BaseCapturerPipeWire::OnCoreError(void* data,
+ uint32_t id,
+ int seq,
+ int res,
+ const char* message) {
+ BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(data);
+ RTC_DCHECK(that);
+
+ RTC_LOG(LS_ERROR) << "PipeWire remote error: " << message;
+}
+
+// static
+void BaseCapturerPipeWire::OnStreamStateChanged(void* data,
+ pw_stream_state old_state,
+ pw_stream_state state,
+ const char* error_message) {
+ BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(data);
+ RTC_DCHECK(that);
+
+ switch (state) {
+ case PW_STREAM_STATE_ERROR:
+ RTC_LOG(LS_ERROR) << "PipeWire stream state error: " << error_message;
+ break;
+ case PW_STREAM_STATE_PAUSED:
+ case PW_STREAM_STATE_STREAMING:
+ case PW_STREAM_STATE_UNCONNECTED:
+ case PW_STREAM_STATE_CONNECTING:
+ break;
+ }
+}
+
+// static
+void BaseCapturerPipeWire::OnStreamParamChanged(void* data,
+ uint32_t id,
+ const struct spa_pod* format) {
+ BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(data);
+ RTC_DCHECK(that);
+
+ RTC_LOG(LS_INFO) << "PipeWire stream format changed.";
+
+ if (!format || id != SPA_PARAM_Format) {
+ return;
+ }
+
+ spa_format_video_raw_parse(format, &that->spa_video_format_);
+
+ auto width = that->spa_video_format_.size.width;
+ auto height = that->spa_video_format_.size.height;
+ // In order to be able to build in the non unified environment kBytesPerPixel
+ // must be fully qualified, see Bug 1725145
+ auto stride = SPA_ROUND_UP_N(width * BasicDesktopFrame::kBytesPerPixel, 4);
+ auto size = height * stride;
+
+ that->desktop_size_ = DesktopSize(width, height);
+
+ uint8_t buffer[1024] = {};
+ auto builder = spa_pod_builder{buffer, sizeof(buffer)};
+
+ // Setup buffers and meta header for new format.
+ const struct spa_pod* params[3];
+ params[0] = reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(
+ &builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
+ SPA_PARAM_BUFFERS_dataType,
+ SPA_POD_CHOICE_FLAGS_Int((1<<SPA_DATA_MemPtr) |
+ (1<<SPA_DATA_MemFd)),
+ SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), SPA_PARAM_BUFFERS_stride,
+ SPA_POD_Int(stride), SPA_PARAM_BUFFERS_buffers,
+ SPA_POD_CHOICE_RANGE_Int(8, 1, 32)));
+ params[1] = reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(
+ &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type,
+ SPA_POD_Id(SPA_META_Header), SPA_PARAM_META_size,
+ SPA_POD_Int(sizeof(struct spa_meta_header))));
+ params[2] = reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(
+ &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type,
+ SPA_POD_Id(SPA_META_VideoCrop), SPA_PARAM_META_size,
+ SPA_POD_Int(sizeof(struct spa_meta_region))));
+ pw_stream_update_params(that->pw_stream_, params, 3);
+}
+
+// static
+void BaseCapturerPipeWire::OnStreamProcess(void* data) {
+ BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(data);
+ RTC_DCHECK(that);
+
+ struct pw_buffer* next_buffer;
+ struct pw_buffer* buffer = nullptr;
+
+ next_buffer = pw_stream_dequeue_buffer(that->pw_stream_);
+ while (next_buffer) {
+ buffer = next_buffer;
+ next_buffer = pw_stream_dequeue_buffer(that->pw_stream_);
+
+ if (next_buffer) {
+ pw_stream_queue_buffer(that->pw_stream_, buffer);
+ }
+ }
+
+ if (!buffer) {
+ return;
+ }
+
+ that->HandleBuffer(buffer);
+
+ pw_stream_queue_buffer(that->pw_stream_, buffer);
+}
+
+BaseCapturerPipeWire::BaseCapturerPipeWire(CaptureSourceType source_type)
+ : capture_source_type_(source_type) {}
+
+BaseCapturerPipeWire::~BaseCapturerPipeWire() {
+ if (pw_main_loop_) {
+ pw_thread_loop_stop(pw_main_loop_);
+ }
+
+ if (pw_stream_) {
+ pw_stream_destroy(pw_stream_);
+ }
+
+ if (pw_core_) {
+ pw_core_disconnect(pw_core_);
+ }
+
+ if (pw_context_) {
+ pw_context_destroy(pw_context_);
+ }
+
+ if (pw_main_loop_) {
+ pw_thread_loop_destroy(pw_main_loop_);
+ }
+
+ if (start_request_signal_id_) {
+ g_dbus_connection_signal_unsubscribe(connection_, start_request_signal_id_);
+ }
+ if (sources_request_signal_id_) {
+ g_dbus_connection_signal_unsubscribe(connection_,
+ sources_request_signal_id_);
+ }
+ if (session_request_signal_id_) {
+ g_dbus_connection_signal_unsubscribe(connection_,
+ session_request_signal_id_);
+ }
+
+ if (session_handle_) {
+ Scoped<GDBusMessage> message(g_dbus_message_new_method_call(
+ kDesktopBusName, session_handle_, kSessionInterfaceName, "Close"));
+ if (message.get()) {
+ Scoped<GError> error;
+ g_dbus_connection_send_message(connection_, message.get(),
+ G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+ /*out_serial=*/nullptr, error.receive());
+ if (error.get()) {
+ RTC_LOG(LS_ERROR) << "Failed to close the session: " << error->message;
+ }
+ }
+ }
+
+ g_free(start_handle_);
+ g_free(sources_handle_);
+ g_free(session_handle_);
+ g_free(portal_handle_);
+
+ if (cancellable_) {
+ g_cancellable_cancel(cancellable_);
+ g_object_unref(cancellable_);
+ cancellable_ = nullptr;
+ }
+
+ if (proxy_) {
+ g_object_unref(proxy_);
+ proxy_ = nullptr;
+ }
+
+ if (pw_fd_ != -1) {
+ close(pw_fd_);
+ }
+}
+
+void BaseCapturerPipeWire::InitPortal() {
+ cancellable_ = g_cancellable_new();
+ g_dbus_proxy_new_for_bus(
+ G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr,
+ kDesktopBusName, kDesktopObjectPath, kScreenCastInterfaceName,
+ cancellable_,
+ reinterpret_cast<GAsyncReadyCallback>(OnProxyRequested), this);
+}
+
+void BaseCapturerPipeWire::InitPipeWire() {
+#if defined(WEBRTC_DLOPEN_PIPEWIRE)
+ StubPathMap paths;
+
+ // Check if the PipeWire library is available.
+ paths[kModulePipewire03].push_back(kPipeWireLib);
+ if (!InitializeStubs(paths)) {
+ RTC_LOG(LS_ERROR) << "Failed to load the PipeWire library and symbols.";
+ portal_init_failed_ = true;
+ return;
+ }
+#endif // defined(WEBRTC_DLOPEN_PIPEWIRE)
+
+ pw_init(/*argc=*/nullptr, /*argc=*/nullptr);
+
+ pw_main_loop_ = pw_thread_loop_new("pipewire-main-loop", nullptr);
+
+ pw_thread_loop_lock(pw_main_loop_);
+
+ pw_context_ =
+ pw_context_new(pw_thread_loop_get_loop(pw_main_loop_), nullptr, 0);
+ if (!pw_context_) {
+ RTC_LOG(LS_ERROR) << "Failed to create PipeWire context";
+ return;
+ }
+
+ pw_core_ = pw_context_connect_fd(pw_context_, pw_fd_, nullptr, 0);
+ if (!pw_core_) {
+ RTC_LOG(LS_ERROR) << "Failed to connect PipeWire context";
+ return;
+ }
+
+ // Initialize event handlers, remote end and stream-related.
+ pw_core_events_.version = PW_VERSION_CORE_EVENTS;
+ pw_core_events_.error = &OnCoreError;
+
+ pw_stream_events_.version = PW_VERSION_STREAM_EVENTS;
+ pw_stream_events_.state_changed = &OnStreamStateChanged;
+ pw_stream_events_.param_changed = &OnStreamParamChanged;
+ pw_stream_events_.process = &OnStreamProcess;
+
+ pw_core_add_listener(pw_core_, &spa_core_listener_, &pw_core_events_, this);
+
+ pw_stream_ = CreateReceivingStream();
+ if (!pw_stream_) {
+ RTC_LOG(LS_ERROR) << "Failed to create PipeWire stream";
+ return;
+ }
+
+ if (pw_thread_loop_start(pw_main_loop_) < 0) {
+ RTC_LOG(LS_ERROR) << "Failed to start main PipeWire loop";
+ portal_init_failed_ = true;
+ }
+
+ pw_thread_loop_unlock(pw_main_loop_);
+
+ RTC_LOG(LS_INFO) << "PipeWire remote opened.";
+}
+
+pw_stream* BaseCapturerPipeWire::CreateReceivingStream() {
+ spa_rectangle pwMinScreenBounds = spa_rectangle{1, 1};
+ spa_rectangle pwMaxScreenBounds = spa_rectangle{UINT32_MAX, UINT32_MAX};
+
+ pw_properties* reuseProps =
+ pw_properties_new_string("pipewire.client.reuse=1");
+ auto stream = pw_stream_new(pw_core_, "webrtc-consume-stream", reuseProps);
+
+ uint8_t buffer[1024] = {};
+ const spa_pod* params[1];
+ spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)};
+
+ params[0] = reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(
+ &builder, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat,
+ SPA_FORMAT_mediaType, SPA_POD_Id(SPA_MEDIA_TYPE_video),
+ SPA_FORMAT_mediaSubtype, SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw),
+ SPA_FORMAT_VIDEO_format,
+ SPA_POD_CHOICE_ENUM_Id(5, SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx,
+ SPA_VIDEO_FORMAT_RGBA, SPA_VIDEO_FORMAT_BGRx,
+ SPA_VIDEO_FORMAT_BGRA),
+ SPA_FORMAT_VIDEO_size,
+ SPA_POD_CHOICE_RANGE_Rectangle(&pwMinScreenBounds, &pwMinScreenBounds,
+ &pwMaxScreenBounds),
+ 0));
+
+ pw_stream_add_listener(stream, &spa_stream_listener_, &pw_stream_events_,
+ this);
+ if (pw_stream_connect(stream, PW_DIRECTION_INPUT, pw_stream_node_id_,
+ PW_STREAM_FLAG_AUTOCONNECT, params, 1) != 0) {
+ RTC_LOG(LS_ERROR) << "Could not connect receiving stream.";
+ portal_init_failed_ = true;
+ return nullptr;
+ }
+
+ return stream;
+}
+
+void BaseCapturerPipeWire::HandleBuffer(pw_buffer* buffer) {
+ spa_buffer* spaBuffer = buffer->buffer;
+ ScopedBuf map;
+ uint8_t* src = nullptr;
+
+ if (spaBuffer->datas[0].chunk->size == 0) {
+ RTC_LOG(LS_ERROR) << "Failed to get video stream: Zero size.";
+ return;
+ }
+
+ if (spaBuffer->datas[0].type == SPA_DATA_MemFd ||
+ spaBuffer->datas[0].type == SPA_DATA_DmaBuf) {
+ map.initialize(
+ static_cast<uint8_t*>(
+ mmap(nullptr,
+ spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
+ PROT_READ, MAP_PRIVATE, spaBuffer->datas[0].fd, 0)),
+ spaBuffer->datas[0].maxsize + spaBuffer->datas[0].mapoffset,
+ spaBuffer->datas[0].type == SPA_DATA_DmaBuf,
+ spaBuffer->datas[0].fd);
+
+ if (!map) {
+ RTC_LOG(LS_ERROR) << "Failed to mmap the memory: "
+ << std::strerror(errno);
+ return;
+ }
+
+ if (spaBuffer->datas[0].type == SPA_DATA_DmaBuf) {
+ SyncDmaBuf(spaBuffer->datas[0].fd, DMA_BUF_SYNC_START);
+ }
+
+ src = SPA_MEMBER(map.get(), spaBuffer->datas[0].mapoffset, uint8_t);
+ } else if (spaBuffer->datas[0].type == SPA_DATA_MemPtr) {
+ src = static_cast<uint8_t*>(spaBuffer->datas[0].data);
+ }
+
+ if (!src) {
+ return;
+ }
+
+ struct spa_meta_region* video_metadata =
+ static_cast<struct spa_meta_region*>(spa_buffer_find_meta_data(
+ spaBuffer, SPA_META_VideoCrop, sizeof(*video_metadata)));
+
+ // Video size from metadata is bigger than an actual video stream size.
+ // The metadata are wrong or we should up-scale the video...in both cases
+ // just quit now.
+ if (video_metadata && (video_metadata->region.size.width >
+ static_cast<uint32_t>(desktop_size_.width()) ||
+ video_metadata->region.size.height >
+ static_cast<uint32_t>(desktop_size_.height()))) {
+ RTC_LOG(LS_ERROR) << "Stream metadata sizes are wrong!";
+ return;
+ }
+
+ // Use video metadata when video size from metadata is set and smaller than
+ // video stream size, so we need to adjust it.
+ bool video_metadata_use = false;
+
+ const struct spa_rectangle* video_metadata_size =
+ video_metadata ? &video_metadata->region.size : nullptr;
+
+ if (video_metadata_size && video_metadata_size->width != 0 &&
+ video_metadata_size->height != 0 &&
+ (static_cast<int>(video_metadata_size->width) < desktop_size_.width() ||
+ static_cast<int>(video_metadata_size->height) <
+ desktop_size_.height())) {
+ video_metadata_use = true;
+ }
+
+ DesktopSize video_size_prev = video_size_;
+ if (video_metadata_use) {
+ video_size_ =
+ DesktopSize(video_metadata_size->width, video_metadata_size->height);
+ } else {
+ video_size_ = desktop_size_;
+ }
+
+ webrtc::MutexLock lock(&current_frame_lock_);
+ if (!current_frame_ || !video_size_.equals(video_size_prev)) {
+ current_frame_ = std::make_unique<uint8_t[]>(
+ video_size_.width() * video_size_.height() * BasicDesktopFrame::kBytesPerPixel);
+ }
+
+ const int32_t dst_stride = video_size_.width() * BasicDesktopFrame::kBytesPerPixel;
+ const int32_t src_stride = spaBuffer->datas[0].chunk->stride;
+
+ if (src_stride != (desktop_size_.width() * BasicDesktopFrame::kBytesPerPixel)) {
+ RTC_LOG(LS_ERROR) << "Got buffer with stride different from screen stride: "
+ << src_stride
+ << " != " << (desktop_size_.width() * BasicDesktopFrame::kBytesPerPixel);
+ portal_init_failed_ = true;
+
+ return;
+ }
+
+ // Adjust source content based on metadata video position
+ if (video_metadata_use &&
+ (video_metadata->region.position.y + video_size_.height() <=
+ desktop_size_.height())) {
+ src += src_stride * video_metadata->region.position.y;
+ }
+ const int x_offset =
+ video_metadata_use &&
+ (video_metadata->region.position.x + video_size_.width() <=
+ desktop_size_.width())
+ ? video_metadata->region.position.x * BasicDesktopFrame::kBytesPerPixel
+ : 0;
+
+ uint8_t* dst = current_frame_.get();
+ for (int i = 0; i < video_size_.height(); ++i) {
+ // Adjust source content based on crop video position if needed
+ src += x_offset;
+ std::memcpy(dst, src, dst_stride);
+ // If both sides decided to go with the RGBx format we need to convert it to
+ // BGRx to match color format expected by WebRTC.
+ if (spa_video_format_.format == SPA_VIDEO_FORMAT_RGBx ||
+ spa_video_format_.format == SPA_VIDEO_FORMAT_RGBA) {
+ ConvertRGBxToBGRx(dst, dst_stride);
+ }
+ src += src_stride - x_offset;
+ dst += dst_stride;
+ }
+}
+
+void BaseCapturerPipeWire::ConvertRGBxToBGRx(uint8_t* frame, uint32_t size) {
+ // Change color format for KDE KWin which uses RGBx and not BGRx
+ for (uint32_t i = 0; i < size; i += 4) {
+ uint8_t tempR = frame[i];
+ uint8_t tempB = frame[i + 2];
+ frame[i] = tempB;
+ frame[i + 2] = tempR;
+ }
+}
+
+guint BaseCapturerPipeWire::SetupRequestResponseSignal(
+ const gchar* object_path,
+ GDBusSignalCallback callback) {
+ return g_dbus_connection_signal_subscribe(
+ connection_, kDesktopBusName, kRequestInterfaceName, "Response",
+ object_path, /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE,
+ callback, this, /*user_data_free_func=*/nullptr);
+}
+
+// static
+void BaseCapturerPipeWire::OnProxyRequested(GObject* /*object*/,
+ GAsyncResult* result,
+ gpointer user_data) {
+ BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(user_data);
+ RTC_DCHECK(that);
+
+ Scoped<GError> error;
+ GDBusProxy* proxy = g_dbus_proxy_new_finish(result, error.receive());
+ if (!proxy) {
+ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+ RTC_LOG(LS_ERROR) << "Failed to create a proxy for the screen cast portal: "
+ << error->message;
+ that->portal_init_failed_ = true;
+ return;
+ }
+ that->proxy_ = proxy;
+ that->connection_ = g_dbus_proxy_get_connection(that->proxy_);
+
+ RTC_LOG(LS_INFO) << "Created proxy for the screen cast portal.";
+ that->SessionRequest();
+}
+
+// static
+gchar* BaseCapturerPipeWire::PrepareSignalHandle(GDBusConnection* connection,
+ const gchar* token) {
+ Scoped<gchar> sender(
+ g_strdup(g_dbus_connection_get_unique_name(connection) + 1));
+ for (int i = 0; sender.get()[i]; i++) {
+ if (sender.get()[i] == '.') {
+ sender.get()[i] = '_';
+ }
+ }
+
+ gchar* handle = g_strconcat(kDesktopRequestObjectPath, "/", sender.get(), "/",
+ token, /*end of varargs*/ nullptr);
+
+ return handle;
+}
+
+void BaseCapturerPipeWire::SessionRequest() {
+ GVariantBuilder builder;
+ Scoped<gchar> variant_string;
+
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+ variant_string =
+ g_strdup_printf("webrtc_session%d", g_random_int_range(0, G_MAXINT));
+ g_variant_builder_add(&builder, "{sv}", "session_handle_token",
+ g_variant_new_string(variant_string.get()));
+ variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT));
+ g_variant_builder_add(&builder, "{sv}", "handle_token",
+ g_variant_new_string(variant_string.get()));
+
+ portal_handle_ = PrepareSignalHandle(connection_, variant_string.get());
+ session_request_signal_id_ = SetupRequestResponseSignal(
+ portal_handle_, OnSessionRequestResponseSignal);
+
+ RTC_LOG(LS_INFO) << "Screen cast session requested.";
+ g_dbus_proxy_call(
+ proxy_, "CreateSession", g_variant_new("(a{sv})", &builder),
+ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_,
+ reinterpret_cast<GAsyncReadyCallback>(OnSessionRequested), this);
+}
+
+// static
+void BaseCapturerPipeWire::OnSessionRequested(GDBusProxy *proxy,
+ GAsyncResult* result,
+ gpointer user_data) {
+ BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(user_data);
+ RTC_DCHECK(that);
+
+ Scoped<GError> error;
+ Scoped<GVariant> variant(
+ g_dbus_proxy_call_finish(proxy, result, error.receive()));
+ if (!variant) {
+ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+ RTC_LOG(LS_ERROR) << "Failed to create a screen cast session: "
+ << error->message;
+ that->portal_init_failed_ = true;
+ return;
+ }
+ RTC_LOG(LS_INFO) << "Initializing the screen cast session.";
+
+ Scoped<gchar> handle;
+ g_variant_get_child(variant.get(), 0, "o", &handle);
+ if (!handle) {
+ RTC_LOG(LS_ERROR) << "Failed to initialize the screen cast session.";
+ if (that->session_request_signal_id_) {
+ g_dbus_connection_signal_unsubscribe(that->connection_,
+ that->session_request_signal_id_);
+ that->session_request_signal_id_ = 0;
+ }
+ that->portal_init_failed_ = true;
+ return;
+ }
+
+ RTC_LOG(LS_INFO) << "Subscribing to the screen cast session.";
+}
+
+// static
+void BaseCapturerPipeWire::OnSessionRequestResponseSignal(
+ GDBusConnection* connection,
+ const gchar* sender_name,
+ const gchar* object_path,
+ const gchar* interface_name,
+ const gchar* signal_name,
+ GVariant* parameters,
+ gpointer user_data) {
+ BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(user_data);
+ RTC_DCHECK(that);
+
+ RTC_LOG(LS_INFO)
+ << "Received response for the screen cast session subscription.";
+
+ guint32 portal_response;
+ Scoped<GVariant> response_data;
+ g_variant_get(parameters, "(u@a{sv})", &portal_response,
+ response_data.receive());
+ g_variant_lookup(response_data.get(), "session_handle", "s",
+ &that->session_handle_);
+
+ if (!that->session_handle_ || portal_response) {
+ RTC_LOG(LS_ERROR)
+ << "Failed to request the screen cast session subscription.";
+ that->portal_init_failed_ = true;
+ return;
+ }
+
+ that->SourcesRequest();
+}
+
+void BaseCapturerPipeWire::SourcesRequest() {
+ GVariantBuilder builder;
+ Scoped<gchar> variant_string;
+
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+ // We want to record monitor content.
+ g_variant_builder_add(
+ &builder, "{sv}", "types",
+ g_variant_new_uint32(static_cast<uint32_t>(capture_source_type_)));
+ // We don't want to allow selection of multiple sources.
+ g_variant_builder_add(&builder, "{sv}", "multiple",
+ g_variant_new_boolean(false));
+
+ Scoped<GVariant> variant(
+ g_dbus_proxy_get_cached_property(proxy_, "AvailableCursorModes"));
+ if (variant.get()) {
+ uint32_t modes = 0;
+ g_variant_get(variant.get(), "u", &modes);
+ // Request mouse cursor to be embedded as part of the stream, otherwise it
+ // is hidden by default. Make request only if this mode is advertised by
+ // the portal implementation.
+ if (modes &
+ static_cast<uint32_t>(BaseCapturerPipeWire::CursorMode::kEmbedded)) {
+ g_variant_builder_add(&builder, "{sv}", "cursor_mode",
+ g_variant_new_uint32(static_cast<uint32_t>(
+ BaseCapturerPipeWire::CursorMode::kEmbedded)));
+ }
+ }
+
+ variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT));
+ g_variant_builder_add(&builder, "{sv}", "handle_token",
+ g_variant_new_string(variant_string.get()));
+
+ sources_handle_ = PrepareSignalHandle(connection_, variant_string.get());
+ sources_request_signal_id_ = SetupRequestResponseSignal(
+ sources_handle_, OnSourcesRequestResponseSignal);
+
+ RTC_LOG(LS_INFO) << "Requesting sources from the screen cast session.";
+ g_dbus_proxy_call(
+ proxy_, "SelectSources",
+ g_variant_new("(oa{sv})", session_handle_, &builder),
+ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_,
+ reinterpret_cast<GAsyncReadyCallback>(OnSourcesRequested), this);
+}
+
+// static
+void BaseCapturerPipeWire::OnSourcesRequested(GDBusProxy *proxy,
+ GAsyncResult* result,
+ gpointer user_data) {
+ BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(user_data);
+ RTC_DCHECK(that);
+
+ Scoped<GError> error;
+ Scoped<GVariant> variant(
+ g_dbus_proxy_call_finish(proxy, result, error.receive()));
+ if (!variant) {
+ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+ RTC_LOG(LS_ERROR) << "Failed to request the sources: " << error->message;
+ that->portal_init_failed_ = true;
+ return;
+ }
+
+ RTC_LOG(LS_INFO) << "Sources requested from the screen cast session.";
+
+ Scoped<gchar> handle;
+ g_variant_get_child(variant.get(), 0, "o", handle.receive());
+ if (!handle) {
+ RTC_LOG(LS_ERROR) << "Failed to initialize the screen cast session.";
+ if (that->sources_request_signal_id_) {
+ g_dbus_connection_signal_unsubscribe(that->connection_,
+ that->sources_request_signal_id_);
+ that->sources_request_signal_id_ = 0;
+ }
+ that->portal_init_failed_ = true;
+ return;
+ }
+
+ RTC_LOG(LS_INFO) << "Subscribed to sources signal.";
+}
+
+// static
+void BaseCapturerPipeWire::OnSourcesRequestResponseSignal(
+ GDBusConnection* connection,
+ const gchar* sender_name,
+ const gchar* object_path,
+ const gchar* interface_name,
+ const gchar* signal_name,
+ GVariant* parameters,
+ gpointer user_data) {
+ BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(user_data);
+ RTC_DCHECK(that);
+
+ RTC_LOG(LS_INFO) << "Received sources signal from session.";
+
+ guint32 portal_response;
+ g_variant_get(parameters, "(u@a{sv})", &portal_response, nullptr);
+ if (portal_response) {
+ RTC_LOG(LS_ERROR)
+ << "Failed to select sources for the screen cast session.";
+ that->portal_init_failed_ = true;
+ return;
+ }
+
+ that->StartRequest();
+}
+
+void BaseCapturerPipeWire::StartRequest() {
+ GVariantBuilder builder;
+ Scoped<gchar> variant_string;
+
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+ variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT));
+ g_variant_builder_add(&builder, "{sv}", "handle_token",
+ g_variant_new_string(variant_string.get()));
+
+ start_handle_ = PrepareSignalHandle(connection_, variant_string.get());
+ start_request_signal_id_ =
+ SetupRequestResponseSignal(start_handle_, OnStartRequestResponseSignal);
+
+ // "Identifier for the application window", this is Wayland, so not "x11:...".
+ const gchar parent_window[] = "";
+
+ RTC_LOG(LS_INFO) << "Starting the screen cast session.";
+ g_dbus_proxy_call(
+ proxy_, "Start",
+ g_variant_new("(osa{sv})", session_handle_, parent_window, &builder),
+ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_,
+ reinterpret_cast<GAsyncReadyCallback>(OnStartRequested), this);
+}
+
+// static
+void BaseCapturerPipeWire::OnStartRequested(GDBusProxy *proxy,
+ GAsyncResult* result,
+ gpointer user_data) {
+ BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(user_data);
+ RTC_DCHECK(that);
+
+ Scoped<GError> error;
+ Scoped<GVariant> variant(
+ g_dbus_proxy_call_finish(proxy, result, error.receive()));
+ if (!variant) {
+ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+ RTC_LOG(LS_ERROR) << "Failed to start the screen cast session: "
+ << error->message;
+ that->portal_init_failed_ = true;
+ return;
+ }
+
+ RTC_LOG(LS_INFO) << "Initializing the start of the screen cast session.";
+
+ Scoped<gchar> handle;
+ g_variant_get_child(variant.get(), 0, "o", handle.receive());
+ if (!handle) {
+ RTC_LOG(LS_ERROR)
+ << "Failed to initialize the start of the screen cast session.";
+ if (that->start_request_signal_id_) {
+ g_dbus_connection_signal_unsubscribe(that->connection_,
+ that->start_request_signal_id_);
+ that->start_request_signal_id_ = 0;
+ }
+ that->portal_init_failed_ = true;
+ return;
+ }
+
+ RTC_LOG(LS_INFO) << "Subscribed to the start signal.";
+}
+
+// static
+void BaseCapturerPipeWire::OnStartRequestResponseSignal(
+ GDBusConnection* connection,
+ const gchar* sender_name,
+ const gchar* object_path,
+ const gchar* interface_name,
+ const gchar* signal_name,
+ GVariant* parameters,
+ gpointer user_data) {
+ BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(user_data);
+ RTC_DCHECK(that);
+
+ RTC_LOG(LS_INFO) << "Start signal received.";
+ guint32 portal_response;
+ Scoped<GVariant> response_data;
+ Scoped<GVariantIter> iter;
+ g_variant_get(parameters, "(u@a{sv})", &portal_response,
+ response_data.receive());
+ if (portal_response || !response_data) {
+ RTC_LOG(LS_ERROR) << "Failed to start the screen cast session.";
+ that->portal_init_failed_ = true;
+ return;
+ }
+
+ // Array of PipeWire streams. See
+ // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml
+ // documentation for <method name="Start">.
+ if (g_variant_lookup(response_data.get(), "streams", "a(ua{sv})",
+ iter.receive())) {
+ Scoped<GVariant> variant;
+
+ while (g_variant_iter_next(iter.get(), "@(ua{sv})", variant.receive())) {
+ guint32 stream_id;
+ guint32 type;
+ Scoped<GVariant> options;
+
+ g_variant_get(variant.get(), "(u@a{sv})", &stream_id, options.receive());
+ RTC_DCHECK(options.get());
+
+ if (g_variant_lookup(options.get(), "source_type", "u", &type)) {
+ that->capture_source_type_ =
+ static_cast<BaseCapturerPipeWire::CaptureSourceType>(type);
+ }
+
+ that->pw_stream_node_id_ = stream_id;
+
+ break;
+ }
+ }
+
+ that->OpenPipeWireRemote();
+}
+
+void BaseCapturerPipeWire::OpenPipeWireRemote() {
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+
+ RTC_LOG(LS_INFO) << "Opening the PipeWire remote.";
+
+ g_dbus_proxy_call_with_unix_fd_list(
+ proxy_, "OpenPipeWireRemote",
+ g_variant_new("(oa{sv})", session_handle_, &builder),
+ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, /*fd_list=*/nullptr,
+ cancellable_,
+ reinterpret_cast<GAsyncReadyCallback>(OnOpenPipeWireRemoteRequested),
+ this);
+}
+
+// static
+void BaseCapturerPipeWire::OnOpenPipeWireRemoteRequested(
+ GDBusProxy *proxy,
+ GAsyncResult* result,
+ gpointer user_data) {
+ BaseCapturerPipeWire* that = static_cast<BaseCapturerPipeWire*>(user_data);
+ RTC_DCHECK(that);
+
+ Scoped<GError> error;
+ Scoped<GUnixFDList> outlist;
+ Scoped<GVariant> variant(g_dbus_proxy_call_with_unix_fd_list_finish(
+ proxy, outlist.receive(), result, error.receive()));
+ if (!variant) {
+ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+ RTC_LOG(LS_ERROR) << "Failed to open the PipeWire remote: "
+ << error->message;
+ that->portal_init_failed_ = true;
+ return;
+ }
+
+ gint32 index;
+ g_variant_get(variant.get(), "(h)", &index);
+
+ if ((that->pw_fd_ =
+ g_unix_fd_list_get(outlist.get(), index, error.receive())) == -1) {
+ RTC_LOG(LS_ERROR) << "Failed to get file descriptor from the list: "
+ << error->message;
+ that->portal_init_failed_ = true;
+ return;
+ }
+
+ that->InitPipeWire();
+}
+
+void BaseCapturerPipeWire::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+
+ InitPortal();
+
+ callback_ = callback;
+}
+
+void BaseCapturerPipeWire::CaptureFrame() {
+ if (portal_init_failed_) {
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+
+ webrtc::MutexLock lock(&current_frame_lock_);
+ if (!current_frame_) {
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+
+ DesktopSize frame_size = video_size_;
+
+ std::unique_ptr<DesktopFrame> result(new BasicDesktopFrame(frame_size));
+ result->CopyPixelsFrom(
+ current_frame_.get(), (frame_size.width() * BasicDesktopFrame::kBytesPerPixel),
+ DesktopRect::MakeWH(frame_size.width(), frame_size.height()));
+ if (!result) {
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+
+ // TODO(julien.isorce): http://crbug.com/945468. Set the icc profile on the
+ // frame, see ScreenCapturerX11::CaptureFrame.
+
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(result));
+}
+
+// Keep in sync with defines at browser/actors/WebRTCParent.jsm
+// With PipeWire we can't select which system resource is shared so
+// we don't create a window/screen list. Instead we place these constants
+// as window name/id so frontend code can identify PipeWire backend
+// and does not try to create screen/window preview.
+
+#define PIPEWIRE_ID 0xaffffff
+#define PIPEWIRE_NAME "####_PIPEWIRE_PORTAL_####"
+
+bool BaseCapturerPipeWire::GetSourceList(SourceList* sources) {
+ sources->push_back({PIPEWIRE_ID, 0, PIPEWIRE_NAME});
+ return true;
+}
+
+bool BaseCapturerPipeWire::SelectSource(SourceId id) {
+ // Screen selection is handled by the xdg-desktop-portal.
+ return id == PIPEWIRE_ID;
+}
+
+// static
+std::unique_ptr<DesktopCapturer> BaseCapturerPipeWire::CreateRawCapturer(
+ const DesktopCaptureOptions& options) {
+ return std::make_unique<BaseCapturerPipeWire>(
+ BaseCapturerPipeWire::CaptureSourceType::kAny);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.h b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.h
new file mode 100644
index 0000000000..a3d43336fd
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.h
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_BASE_CAPTURER_PIPEWIRE_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_BASE_CAPTURER_PIPEWIRE_H_
+#include <gio/gio.h>
+#define typeof __typeof__
+#include <pipewire/pipewire.h>
+#include <spa/param/video/format-utils.h>
+#include <spa/utils/result.h>
+
+#include "absl/types/optional.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "rtc_base/synchronization/mutex.h"
+
+namespace webrtc {
+
+class BaseCapturerPipeWire : public DesktopCapturer {
+ public:
+ // Values are set based on source type property in
+ // xdg-desktop-portal/screencast
+ // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml
+ enum class CaptureSourceType : uint32_t {
+ kScreen = 0b01,
+ kWindow = 0b10,
+ kAny = 0b11
+ };
+
+ enum class CursorMode : uint32_t {
+ kHidden = 0b01,
+ kEmbedded = 0b10,
+ kMetadata = 0b100
+ };
+
+ explicit BaseCapturerPipeWire(CaptureSourceType source_type);
+ ~BaseCapturerPipeWire() override;
+
+ BaseCapturerPipeWire(const BaseCapturerPipeWire&) = delete;
+ BaseCapturerPipeWire& operator=(const BaseCapturerPipeWire&) = delete;
+
+ static std::unique_ptr<DesktopCapturer> CreateRawCapturer(
+ const DesktopCaptureOptions& options);
+
+ // DesktopCapturer interface.
+ void Start(Callback* delegate) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+
+ private:
+ // PipeWire types -->
+ struct pw_context* pw_context_ = nullptr;
+ struct pw_core* pw_core_ = nullptr;
+ struct pw_stream* pw_stream_ = nullptr;
+ struct pw_thread_loop* pw_main_loop_ = nullptr;
+
+ spa_hook spa_core_listener_;
+ spa_hook spa_stream_listener_;
+
+ // event handlers
+ pw_core_events pw_core_events_ = {};
+ pw_stream_events pw_stream_events_ = {};
+
+ struct spa_video_info_raw spa_video_format_;
+
+ guint32 pw_stream_node_id_ = 0;
+ gint32 pw_fd_ = -1;
+
+ CaptureSourceType capture_source_type_ =
+ BaseCapturerPipeWire::CaptureSourceType::kScreen;
+
+ // <-- end of PipeWire types
+
+ GDBusConnection* connection_ = nullptr;
+ GDBusProxy* proxy_ = nullptr;
+ GCancellable *cancellable_ = nullptr;
+ gchar* portal_handle_ = nullptr;
+ gchar* session_handle_ = nullptr;
+ gchar* sources_handle_ = nullptr;
+ gchar* start_handle_ = nullptr;
+ guint session_request_signal_id_ = 0;
+ guint sources_request_signal_id_ = 0;
+ guint start_request_signal_id_ = 0;
+
+ DesktopSize video_size_;
+ DesktopSize desktop_size_ = {};
+ DesktopCaptureOptions options_ = {};
+
+ webrtc::Mutex current_frame_lock_;
+ std::unique_ptr<uint8_t[]> current_frame_;
+ Callback* callback_ = nullptr;
+
+ bool portal_init_failed_ = false;
+
+ void InitPortal();
+ void InitPipeWire();
+ void InitPipeWireTypes();
+
+ pw_stream* CreateReceivingStream();
+ void HandleBuffer(pw_buffer* buffer);
+
+ void ConvertRGBxToBGRx(uint8_t* frame, uint32_t size);
+
+ static void OnCoreError(void* data,
+ uint32_t id,
+ int seq,
+ int res,
+ const char* message);
+ static void OnStreamParamChanged(void* data,
+ uint32_t id,
+ const struct spa_pod* format);
+ static void OnStreamStateChanged(void* data,
+ pw_stream_state old_state,
+ pw_stream_state state,
+ const char* error_message);
+
+ static void OnStreamProcess(void* data);
+ static void OnNewBuffer(void* data, uint32_t id);
+
+ guint SetupRequestResponseSignal(const gchar* object_path,
+ GDBusSignalCallback callback);
+
+ static void OnProxyRequested(GObject* object,
+ GAsyncResult* result,
+ gpointer user_data);
+
+ static gchar* PrepareSignalHandle(GDBusConnection* connection,
+ const gchar* token);
+
+ void SessionRequest();
+ static void OnSessionRequested(GDBusProxy *proxy,
+ GAsyncResult* result,
+ gpointer user_data);
+ static void OnSessionRequestResponseSignal(GDBusConnection* connection,
+ const gchar* sender_name,
+ const gchar* object_path,
+ const gchar* interface_name,
+ const gchar* signal_name,
+ GVariant* parameters,
+ gpointer user_data);
+
+ void SourcesRequest();
+ static void OnSourcesRequested(GDBusProxy *proxy,
+ GAsyncResult* result,
+ gpointer user_data);
+ static void OnSourcesRequestResponseSignal(GDBusConnection* connection,
+ const gchar* sender_name,
+ const gchar* object_path,
+ const gchar* interface_name,
+ const gchar* signal_name,
+ GVariant* parameters,
+ gpointer user_data);
+
+ void StartRequest();
+ static void OnStartRequested(GDBusProxy *proxy,
+ GAsyncResult* result,
+ gpointer user_data);
+ static void OnStartRequestResponseSignal(GDBusConnection* connection,
+ const gchar* sender_name,
+ const gchar* object_path,
+ const gchar* interface_name,
+ const gchar* signal_name,
+ GVariant* parameters,
+ gpointer user_data);
+
+ void OpenPipeWireRemote();
+ static void OnOpenPipeWireRemoteRequested(GDBusProxy *proxy,
+ GAsyncResult* result,
+ gpointer user_data);
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_BASE_CAPTURER_PIPEWIRE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/pipewire.sigs b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/pipewire.sigs
new file mode 100644
index 0000000000..06a97b8f29
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/pipewire.sigs
@@ -0,0 +1,50 @@
+// Copyright 2018 The WebRTC project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+//------------------------------------------------
+// Functions from PipeWire used in capturer code.
+//------------------------------------------------
+
+// core.h
+int pw_core_disconnect(pw_core *core);
+
+// loop.h
+void pw_loop_destroy(pw_loop *loop);
+pw_loop * pw_loop_new(const spa_dict *props);
+
+
+// pipewire.h
+void pw_init(int *argc, char **argv[]);
+const char* pw_get_library_version();
+
+// properties.h
+pw_properties * pw_properties_new_string(const char *args);
+
+// stream.h
+void pw_stream_add_listener(pw_stream *stream, spa_hook *listener, const pw_stream_events *events, void *data);
+int pw_stream_connect(pw_stream *stream, enum pw_direction direction, uint32_t target_id, enum pw_stream_flags flags, const spa_pod **params, uint32_t n_params);
+int pw_stream_disconnect(pw_stream *stream);
+pw_buffer *pw_stream_dequeue_buffer(pw_stream *stream);
+void pw_stream_destroy(pw_stream *stream);
+pw_stream * pw_stream_new(pw_core *core, const char *name, pw_properties *props);
+int pw_stream_queue_buffer(pw_stream *stream, pw_buffer *buffer);
+int pw_stream_set_active(pw_stream *stream, bool active);
+int pw_stream_update_params(pw_stream *stream, const spa_pod **params, uint32_t n_params);
+
+// thread-loop.h
+void pw_thread_loop_destroy(pw_thread_loop *loop);
+pw_thread_loop * pw_thread_loop_new(const char *name, const spa_dict *props);
+int pw_thread_loop_start(pw_thread_loop *loop);
+void pw_thread_loop_stop(pw_thread_loop *loop);
+void pw_thread_loop_lock(pw_thread_loop *loop);
+void pw_thread_loop_unlock(pw_thread_loop *loop);
+pw_loop * pw_thread_loop_get_loop(pw_thread_loop *loop);
+void pw_thread_loop_signal(pw_thread_loop *loop, bool wait_for_accept);
+void pw_thread_loop_wait(pw_thread_loop *loop);
+
+// context.h
+void pw_context_destroy(pw_context *context);
+pw_context *pw_context_new(pw_loop *main_loop, pw_properties *props, size_t user_data_size);
+pw_core * pw_context_connect(pw_context *context, pw_properties *properties, size_t user_data_size);
+pw_core * pw_context_connect_fd(pw_context *context, int fd, pw_properties *properties, size_t user_data_size);
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/pipewire_stub_header.fragment b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/pipewire_stub_header.fragment
new file mode 100644
index 0000000000..06ae18dfd4
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/pipewire_stub_header.fragment
@@ -0,0 +1,9 @@
+// The extra include header needed in the generated stub file for defining
+// various PipeWire types.
+
+extern "C" {
+
+#include <pipewire/pipewire.h>
+
+#include <xf86drm.h>
+}
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/portal_request_response.h b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/portal_request_response.h
new file mode 100644
index 0000000000..dde9ac5eff
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/portal_request_response.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_PORTAL_REQUEST_RESPONSE_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_PORTAL_REQUEST_RESPONSE_H_
+
+namespace webrtc {
+namespace xdg_portal {
+
+// Contains type of responses that can be observed when making a request to
+// a desktop portal interface.
+enum class RequestResponse {
+ // Unknown, the initialized status.
+ kUnknown,
+ // Success, the request is carried out.
+ kSuccess,
+ // The user cancelled the interaction.
+ kUserCancelled,
+ // The user interaction was ended in some other way.
+ kError,
+
+ kMaxValue = kError,
+};
+
+} // namespace xdg_portal
+} // namespace webrtc
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_PORTAL_REQUEST_RESPONSE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/restore_token_manager.cc b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/restore_token_manager.cc
new file mode 100644
index 0000000000..cc626d3065
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/restore_token_manager.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/wayland/restore_token_manager.h"
+
+namespace webrtc {
+
+// static
+RestoreTokenManager& RestoreTokenManager::GetInstance() {
+ static webrtc::RestoreTokenManager* manager = new RestoreTokenManager();
+ return *manager;
+}
+
+void RestoreTokenManager::AddToken(DesktopCapturer::SourceId id,
+ const std::string& token) {
+ restore_tokens_.insert({id, token});
+}
+
+std::string RestoreTokenManager::TakeToken(DesktopCapturer::SourceId id) {
+ std::string token = restore_tokens_[id];
+ // Remove the token as it cannot be used anymore
+ restore_tokens_.erase(id);
+ return token;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/restore_token_manager.h b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/restore_token_manager.h
new file mode 100644
index 0000000000..37c9a39cac
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/restore_token_manager.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_RESTORE_TOKEN_MANAGER_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_RESTORE_TOKEN_MANAGER_H_
+
+#include <mutex>
+#include <string>
+#include <unordered_map>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+
+namespace webrtc {
+
+class RestoreTokenManager {
+ public:
+ RestoreTokenManager(const RestoreTokenManager& manager) = delete;
+ RestoreTokenManager& operator=(const RestoreTokenManager& manager) = delete;
+
+ static RestoreTokenManager& GetInstance();
+
+ void AddToken(DesktopCapturer::SourceId id, const std::string& token);
+ std::string TakeToken(DesktopCapturer::SourceId id);
+
+ private:
+ RestoreTokenManager() = default;
+ ~RestoreTokenManager() = default;
+
+ std::unordered_map<DesktopCapturer::SourceId, std::string> restore_tokens_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_RESTORE_TOKEN_MANAGER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/scoped_glib.cc b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/scoped_glib.cc
new file mode 100644
index 0000000000..0d9a87d7fd
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/scoped_glib.cc
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/wayland/scoped_glib.h"
+
+namespace webrtc {
+
+template <>
+Scoped<GError>::~Scoped() {
+ if (ptr_) {
+ g_error_free(ptr_);
+ }
+}
+
+template <>
+Scoped<char>::~Scoped() {
+ if (ptr_) {
+ g_free(ptr_);
+ }
+}
+
+template <>
+Scoped<GVariant>::~Scoped() {
+ if (ptr_) {
+ g_variant_unref(ptr_);
+ }
+}
+
+template <>
+Scoped<GVariantIter>::~Scoped() {
+ if (ptr_) {
+ g_variant_iter_free(ptr_);
+ }
+}
+
+template <>
+Scoped<GDBusMessage>::~Scoped() {
+ if (ptr_) {
+ g_object_unref(ptr_);
+ }
+}
+
+template <>
+Scoped<GUnixFDList>::~Scoped() {
+ if (ptr_) {
+ g_object_unref(ptr_);
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/scoped_glib.h b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/scoped_glib.h
new file mode 100644
index 0000000000..908bd6f77d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/scoped_glib.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCOPED_GLIB_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCOPED_GLIB_H_
+
+#include <gio/gio.h>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+template <class T>
+class Scoped {
+ public:
+ Scoped() {}
+ explicit Scoped(T* val) { ptr_ = val; }
+ ~Scoped() { RTC_DCHECK_NOTREACHED(); }
+
+ T* operator->() const { return ptr_; }
+
+ explicit operator bool() const { return ptr_ != nullptr; }
+
+ bool operator!() const { return ptr_ == nullptr; }
+
+ T* get() const { return ptr_; }
+
+ T** receive() {
+ RTC_CHECK(!ptr_);
+ return &ptr_;
+ }
+
+ Scoped& operator=(T* val) {
+ RTC_DCHECK(val);
+ ptr_ = val;
+ return *this;
+ }
+
+ protected:
+ T* ptr_ = nullptr;
+};
+
+template <>
+Scoped<GError>::~Scoped();
+template <>
+Scoped<char>::~Scoped();
+template <>
+Scoped<GVariant>::~Scoped();
+template <>
+Scoped<GVariantIter>::~Scoped();
+template <>
+Scoped<GDBusMessage>::~Scoped();
+template <>
+Scoped<GUnixFDList>::~Scoped();
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCOPED_GLIB_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc
new file mode 100644
index 0000000000..02d9d2e806
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.cc
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#include "modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h"
+#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
+
+#include <string>
+
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace xdg_portal {
+
+void ScreenCapturePortalInterface::RequestSessionUsingProxy(
+ GAsyncResult* result) {
+ Scoped<GError> error;
+ GDBusProxy* proxy = g_dbus_proxy_new_finish(result, error.receive());
+ if (!proxy) {
+ // Ignore the error caused by user cancelling the request via `cancellable_`
+ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+ RTC_LOG(LS_ERROR) << "Failed to get a proxy for the portal: "
+ << error->message;
+ OnPortalDone(RequestResponse::kError);
+ return;
+ }
+
+ RTC_LOG(LS_INFO) << "Successfully created proxy for the portal.";
+ RequestSession(proxy);
+}
+
+void ScreenCapturePortalInterface::OnSessionRequestResult(
+ GDBusProxy* proxy,
+ GAsyncResult* result) {
+ Scoped<GError> error;
+ Scoped<GVariant> variant(
+ g_dbus_proxy_call_finish(proxy, result, error.receive()));
+ if (!variant) {
+ // Ignore the error caused by user cancelling the request via `cancellable_`
+ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+ RTC_LOG(LS_ERROR) << "Failed to request session: " << error->message;
+ OnPortalDone(RequestResponse::kError);
+ return;
+ }
+
+ RTC_LOG(LS_INFO) << "Initializing the session.";
+
+ Scoped<char> handle;
+ g_variant_get_child(variant.get(), /*index=*/0, /*format_string=*/"o",
+ &handle);
+ if (!handle) {
+ RTC_LOG(LS_ERROR) << "Failed to initialize the session.";
+ OnPortalDone(RequestResponse::kError);
+ return;
+ }
+}
+
+void ScreenCapturePortalInterface::RegisterSessionClosedSignalHandler(
+ const SessionClosedSignalHandler session_close_signal_handler,
+ GVariant* parameters,
+ GDBusConnection* connection,
+ std::string& session_handle,
+ guint& session_closed_signal_id) {
+ uint32_t portal_response = 2;
+ Scoped<GVariant> response_data;
+ g_variant_get(parameters, /*format_string=*/"(u@a{sv})", &portal_response,
+ response_data.receive());
+
+ if (RequestResponseFromPortalResponse(portal_response) !=
+ RequestResponse::kSuccess) {
+ RTC_LOG(LS_ERROR) << "Failed to request the session subscription.";
+ OnPortalDone(RequestResponse::kError);
+ return;
+ }
+
+ Scoped<GVariant> g_session_handle(
+ g_variant_lookup_value(response_data.get(), /*key=*/"session_handle",
+ /*expected_type=*/nullptr));
+ session_handle = g_variant_get_string(
+ /*value=*/g_session_handle.get(), /*length=*/nullptr);
+
+ if (session_handle.empty()) {
+ RTC_LOG(LS_ERROR) << "Could not get session handle despite valid response";
+ OnPortalDone(RequestResponse::kError);
+ return;
+ }
+
+ session_closed_signal_id = g_dbus_connection_signal_subscribe(
+ connection, kDesktopBusName, kSessionInterfaceName, /*member=*/"Closed",
+ session_handle.c_str(), /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NONE,
+ session_close_signal_handler, this, /*user_data_free_func=*/nullptr);
+}
+
+void ScreenCapturePortalInterface::OnStartRequestResult(GDBusProxy* proxy,
+ GAsyncResult* result) {
+ Scoped<GError> error;
+ Scoped<GVariant> variant(
+ g_dbus_proxy_call_finish(proxy, result, error.receive()));
+ if (!variant) {
+ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+ RTC_LOG(LS_ERROR) << "Failed to start the portal session: "
+ << error->message;
+ OnPortalDone(RequestResponse::kError);
+ return;
+ }
+
+ Scoped<char> handle;
+ g_variant_get_child(variant.get(), 0, "o", handle.receive());
+ if (!handle) {
+ RTC_LOG(LS_ERROR) << "Failed to initialize the start portal session.";
+ OnPortalDone(RequestResponse::kError);
+ return;
+ }
+
+ RTC_LOG(LS_INFO) << "Subscribed to the start signal.";
+}
+
+} // namespace xdg_portal
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h
new file mode 100644
index 0000000000..775ed1facc
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREEN_CAPTURE_PORTAL_INTERFACE_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREEN_CAPTURE_PORTAL_INTERFACE_H_
+
+#include <gio/gio.h>
+
+#include <string>
+
+#include "modules/desktop_capture/linux/wayland/portal_request_response.h"
+#include "modules/desktop_capture/linux/wayland/scoped_glib.h"
+#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
+#include "modules/desktop_capture/linux/wayland/xdg_session_details.h"
+
+namespace webrtc {
+namespace xdg_portal {
+
+using SessionClosedSignalHandler = void (*)(GDBusConnection*,
+ const char*,
+ const char*,
+ const char*,
+ const char*,
+ GVariant*,
+ gpointer);
+
+// A base class for XDG desktop portals that can capture desktop/screen.
+// Note: downstream clients inherit from this class so it is advisable to
+// provide a default implementation of any new virtual methods that may be added
+// to this class.
+class ScreenCapturePortalInterface {
+ public:
+ virtual ~ScreenCapturePortalInterface() {}
+ // Gets details about the session such as session handle.
+ virtual xdg_portal::SessionDetails GetSessionDetails() { return {}; }
+ // Starts the portal setup.
+ virtual void Start() {}
+ // Notifies observers about the success/fail state of the portal
+ // request/response.
+ virtual void OnPortalDone(xdg_portal::RequestResponse result) {}
+ // Sends a create session request to the portal.
+ virtual void RequestSession(GDBusProxy* proxy) {}
+
+ // Following methods should not be made virtual as they share a common
+ // implementation between portals.
+
+ // Requests portal session using the proxy object.
+ void RequestSessionUsingProxy(GAsyncResult* result);
+ // Handles the session request result.
+ void OnSessionRequestResult(GDBusProxy* proxy, GAsyncResult* result);
+ // Subscribes to session close signal and sets up a handler for it.
+ void RegisterSessionClosedSignalHandler(
+ const SessionClosedSignalHandler session_close_signal_handler,
+ GVariant* parameters,
+ GDBusConnection* connection,
+ std::string& session_handle,
+ guint& session_closed_signal_id);
+ // Handles the result of session start request.
+ void OnStartRequestResult(GDBusProxy* proxy, GAsyncResult* result);
+};
+
+} // namespace xdg_portal
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREEN_CAPTURE_PORTAL_INTERFACE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_portal.cc b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_portal.cc
new file mode 100644
index 0000000000..8e45af7e24
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_portal.cc
@@ -0,0 +1,455 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/wayland/screencast_portal.h"
+
+#include <gio/gunixfdlist.h>
+#include <glib-object.h>
+
+#include "modules/desktop_capture/linux/wayland/scoped_glib.h"
+#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace {
+
+using xdg_portal::kScreenCastInterfaceName;
+using xdg_portal::PrepareSignalHandle;
+using xdg_portal::RequestResponse;
+using xdg_portal::RequestSessionProxy;
+using xdg_portal::SetupRequestResponseSignal;
+using xdg_portal::SetupSessionRequestHandlers;
+using xdg_portal::StartSessionRequest;
+using xdg_portal::TearDownSession;
+using xdg_portal::RequestResponseFromPortalResponse;
+
+} // namespace
+
+ScreenCastPortal::ScreenCastPortal(
+ ScreenCastPortal::CaptureSourceType source_type,
+ PortalNotifier* notifier)
+ : ScreenCastPortal(source_type,
+ notifier,
+ OnProxyRequested,
+ OnSourcesRequestResponseSignal,
+ this) {}
+
+ScreenCastPortal::ScreenCastPortal(
+ CaptureSourceType source_type,
+ PortalNotifier* notifier,
+ ProxyRequestResponseHandler proxy_request_response_handler,
+ SourcesRequestResponseSignalHandler sources_request_response_signal_handler,
+ gpointer user_data)
+ : notifier_(notifier),
+ capture_source_type_(source_type),
+ proxy_request_response_handler_(proxy_request_response_handler),
+ sources_request_response_signal_handler_(
+ sources_request_response_signal_handler),
+ user_data_(user_data) {}
+
+ScreenCastPortal::~ScreenCastPortal() {
+ Cleanup();
+}
+
+void ScreenCastPortal::Cleanup() {
+ UnsubscribeSignalHandlers();
+ TearDownSession(std::move(session_handle_), proxy_, cancellable_,
+ connection_);
+ session_handle_ = "";
+ cancellable_ = nullptr;
+ proxy_ = nullptr;
+
+ if (pw_fd_ != -1) {
+ close(pw_fd_);
+ }
+}
+
+void ScreenCastPortal::UnsubscribeSignalHandlers() {
+ if (start_request_signal_id_) {
+ g_dbus_connection_signal_unsubscribe(connection_, start_request_signal_id_);
+ start_request_signal_id_ = 0;
+ }
+
+ if (sources_request_signal_id_) {
+ g_dbus_connection_signal_unsubscribe(connection_,
+ sources_request_signal_id_);
+ sources_request_signal_id_ = 0;
+ }
+
+ if (session_request_signal_id_) {
+ g_dbus_connection_signal_unsubscribe(connection_,
+ session_request_signal_id_);
+ session_request_signal_id_ = 0;
+ }
+}
+
+void ScreenCastPortal::SetSessionDetails(
+ const xdg_portal::SessionDetails& session_details) {
+ if (session_details.proxy) {
+ proxy_ = session_details.proxy;
+ connection_ = g_dbus_proxy_get_connection(proxy_);
+ }
+ if (session_details.cancellable) {
+ cancellable_ = session_details.cancellable;
+ }
+ if (!session_details.session_handle.empty()) {
+ session_handle_ = session_details.session_handle;
+ }
+ if (session_details.pipewire_stream_node_id) {
+ pw_stream_node_id_ = session_details.pipewire_stream_node_id;
+ }
+}
+
+void ScreenCastPortal::Start() {
+ cancellable_ = g_cancellable_new();
+ RequestSessionProxy(kScreenCastInterfaceName, proxy_request_response_handler_,
+ cancellable_, this);
+}
+
+xdg_portal::SessionDetails ScreenCastPortal::GetSessionDetails() {
+ return {}; // No-op
+}
+
+void ScreenCastPortal::OnPortalDone(RequestResponse result) {
+ notifier_->OnScreenCastRequestResult(result, pw_stream_node_id_, pw_fd_);
+ if (result != RequestResponse::kSuccess) {
+ Cleanup();
+ }
+}
+
+// static
+void ScreenCastPortal::OnProxyRequested(GObject* gobject,
+ GAsyncResult* result,
+ gpointer user_data) {
+ static_cast<ScreenCastPortal*>(user_data)->RequestSessionUsingProxy(result);
+}
+
+void ScreenCastPortal::RequestSession(GDBusProxy* proxy) {
+ proxy_ = proxy;
+ connection_ = g_dbus_proxy_get_connection(proxy_);
+ SetupSessionRequestHandlers(
+ "webrtc", OnSessionRequested, OnSessionRequestResponseSignal, connection_,
+ proxy_, cancellable_, portal_handle_, session_request_signal_id_, this);
+}
+
+// static
+void ScreenCastPortal::OnSessionRequested(GDBusProxy* proxy,
+ GAsyncResult* result,
+ gpointer user_data) {
+ static_cast<ScreenCastPortal*>(user_data)->OnSessionRequestResult(proxy,
+ result);
+}
+
+// static
+void ScreenCastPortal::OnSessionRequestResponseSignal(
+ GDBusConnection* connection,
+ const char* sender_name,
+ const char* object_path,
+ const char* interface_name,
+ const char* signal_name,
+ GVariant* parameters,
+ gpointer user_data) {
+ ScreenCastPortal* that = static_cast<ScreenCastPortal*>(user_data);
+ RTC_DCHECK(that);
+ that->RegisterSessionClosedSignalHandler(
+ OnSessionClosedSignal, parameters, that->connection_,
+ that->session_handle_, that->session_closed_signal_id_);
+
+ // Do not continue if we don't get session_handle back. The call above will
+ // already notify the capturer there is a failure, but we would still continue
+ // to make following request and crash on that.
+ if (!that->session_handle_.empty()) {
+ that->SourcesRequest();
+ }
+}
+
+// static
+void ScreenCastPortal::OnSessionClosedSignal(GDBusConnection* connection,
+ const char* sender_name,
+ const char* object_path,
+ const char* interface_name,
+ const char* signal_name,
+ GVariant* parameters,
+ gpointer user_data) {
+ ScreenCastPortal* that = static_cast<ScreenCastPortal*>(user_data);
+ RTC_DCHECK(that);
+
+ RTC_LOG(LS_INFO) << "Received closed signal from session.";
+
+ that->notifier_->OnScreenCastSessionClosed();
+
+ // Unsubscribe from the signal and free the session handle to avoid calling
+ // Session::Close from the destructor since it's already closed
+ g_dbus_connection_signal_unsubscribe(that->connection_,
+ that->session_closed_signal_id_);
+}
+
+void ScreenCastPortal::SourcesRequest() {
+ GVariantBuilder builder;
+ Scoped<char> variant_string;
+
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+ // We want to record monitor content.
+ g_variant_builder_add(
+ &builder, "{sv}", "types",
+ g_variant_new_uint32(static_cast<uint32_t>(capture_source_type_)));
+ // We don't want to allow selection of multiple sources.
+ g_variant_builder_add(&builder, "{sv}", "multiple",
+ g_variant_new_boolean(false));
+
+ Scoped<GVariant> cursorModesVariant(
+ g_dbus_proxy_get_cached_property(proxy_, "AvailableCursorModes"));
+ if (cursorModesVariant.get()) {
+ uint32_t modes = 0;
+ g_variant_get(cursorModesVariant.get(), "u", &modes);
+ // Make request only if this mode is advertised by the portal
+ // implementation.
+ if (modes & static_cast<uint32_t>(cursor_mode_)) {
+ g_variant_builder_add(
+ &builder, "{sv}", "cursor_mode",
+ g_variant_new_uint32(static_cast<uint32_t>(cursor_mode_)));
+ }
+ }
+
+ Scoped<GVariant> versionVariant(
+ g_dbus_proxy_get_cached_property(proxy_, "version"));
+ if (versionVariant.get()) {
+ uint32_t version = 0;
+ g_variant_get(versionVariant.get(), "u", &version);
+ // Make request only if xdg-desktop-portal has required API version
+ if (version >= 4) {
+ g_variant_builder_add(
+ &builder, "{sv}", "persist_mode",
+ g_variant_new_uint32(static_cast<uint32_t>(persist_mode_)));
+ if (!restore_token_.empty()) {
+ g_variant_builder_add(&builder, "{sv}", "restore_token",
+ g_variant_new_string(restore_token_.c_str()));
+ }
+ }
+ }
+
+ variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT));
+ g_variant_builder_add(&builder, "{sv}", "handle_token",
+ g_variant_new_string(variant_string.get()));
+
+ sources_handle_ = PrepareSignalHandle(variant_string.get(), connection_);
+ sources_request_signal_id_ = SetupRequestResponseSignal(
+ sources_handle_.c_str(), sources_request_response_signal_handler_,
+ user_data_, connection_);
+
+ RTC_LOG(LS_INFO) << "Requesting sources from the screen cast session.";
+ g_dbus_proxy_call(
+ proxy_, "SelectSources",
+ g_variant_new("(oa{sv})", session_handle_.c_str(), &builder),
+ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable_,
+ reinterpret_cast<GAsyncReadyCallback>(OnSourcesRequested), this);
+}
+
+// static
+void ScreenCastPortal::OnSourcesRequested(GDBusProxy* proxy,
+ GAsyncResult* result,
+ gpointer user_data) {
+ ScreenCastPortal* that = static_cast<ScreenCastPortal*>(user_data);
+ RTC_DCHECK(that);
+
+ Scoped<GError> error;
+ Scoped<GVariant> variant(
+ g_dbus_proxy_call_finish(proxy, result, error.receive()));
+ if (!variant) {
+ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+ RTC_LOG(LS_ERROR) << "Failed to request the sources: " << error->message;
+ that->OnPortalDone(RequestResponse::kError);
+ return;
+ }
+
+ RTC_LOG(LS_INFO) << "Sources requested from the screen cast session.";
+
+ Scoped<char> handle;
+ g_variant_get_child(variant.get(), 0, "o", handle.receive());
+ if (!handle) {
+ RTC_LOG(LS_ERROR) << "Failed to initialize the screen cast session.";
+ if (that->sources_request_signal_id_) {
+ g_dbus_connection_signal_unsubscribe(that->connection_,
+ that->sources_request_signal_id_);
+ that->sources_request_signal_id_ = 0;
+ }
+ that->OnPortalDone(RequestResponse::kError);
+ return;
+ }
+
+ RTC_LOG(LS_INFO) << "Subscribed to sources signal.";
+}
+
+// static
+void ScreenCastPortal::OnSourcesRequestResponseSignal(
+ GDBusConnection* connection,
+ const char* sender_name,
+ const char* object_path,
+ const char* interface_name,
+ const char* signal_name,
+ GVariant* parameters,
+ gpointer user_data) {
+ ScreenCastPortal* that = static_cast<ScreenCastPortal*>(user_data);
+ RTC_DCHECK(that);
+
+ RTC_LOG(LS_INFO) << "Received sources signal from session.";
+
+ uint32_t portal_response;
+ g_variant_get(parameters, "(u@a{sv})", &portal_response, nullptr);
+ if (portal_response) {
+ RTC_LOG(LS_ERROR)
+ << "Failed to select sources for the screen cast session.";
+ that->OnPortalDone(RequestResponse::kError);
+ return;
+ }
+
+ that->StartRequest();
+}
+
+void ScreenCastPortal::StartRequest() {
+ StartSessionRequest("webrtc", session_handle_, OnStartRequestResponseSignal,
+ OnStartRequested, proxy_, connection_, cancellable_,
+ start_request_signal_id_, start_handle_, this);
+}
+
+// static
+void ScreenCastPortal::OnStartRequested(GDBusProxy* proxy,
+ GAsyncResult* result,
+ gpointer user_data) {
+ static_cast<ScreenCastPortal*>(user_data)->OnStartRequestResult(proxy,
+ result);
+}
+
+// static
+void ScreenCastPortal::OnStartRequestResponseSignal(GDBusConnection* connection,
+ const char* sender_name,
+ const char* object_path,
+ const char* interface_name,
+ const char* signal_name,
+ GVariant* parameters,
+ gpointer user_data) {
+ ScreenCastPortal* that = static_cast<ScreenCastPortal*>(user_data);
+ RTC_DCHECK(that);
+
+ RTC_LOG(LS_INFO) << "Start signal received.";
+ uint32_t portal_response;
+ Scoped<GVariant> response_data;
+ Scoped<GVariantIter> iter;
+ Scoped<char> restore_token;
+ g_variant_get(parameters, "(u@a{sv})", &portal_response,
+ response_data.receive());
+ if (portal_response || !response_data) {
+ RTC_LOG(LS_ERROR) << "Failed to start the screen cast session.";
+ that->OnPortalDone(RequestResponseFromPortalResponse(portal_response));
+ return;
+ }
+
+ // Array of PipeWire streams. See
+ // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml
+ // documentation for <method name="Start">.
+ if (g_variant_lookup(response_data.get(), "streams", "a(ua{sv})",
+ iter.receive())) {
+ Scoped<GVariant> variant;
+
+ while (g_variant_iter_next(iter.get(), "@(ua{sv})", variant.receive())) {
+ uint32_t stream_id;
+ uint32_t type;
+ Scoped<GVariant> options;
+
+ g_variant_get(variant.get(), "(u@a{sv})", &stream_id, options.receive());
+ RTC_DCHECK(options.get());
+
+ if (g_variant_lookup(options.get(), "source_type", "u", &type)) {
+ that->capture_source_type_ =
+ static_cast<ScreenCastPortal::CaptureSourceType>(type);
+ }
+
+ that->pw_stream_node_id_ = stream_id;
+
+ break;
+ }
+ }
+
+ if (g_variant_lookup(response_data.get(), "restore_token", "s",
+ restore_token.receive())) {
+ that->restore_token_ = restore_token.get();
+ }
+
+ that->OpenPipeWireRemote();
+}
+
+uint32_t ScreenCastPortal::pipewire_stream_node_id() {
+ return pw_stream_node_id_;
+}
+
+void ScreenCastPortal::SetPersistMode(ScreenCastPortal::PersistMode mode) {
+ persist_mode_ = mode;
+}
+
+void ScreenCastPortal::SetRestoreToken(const std::string& token) {
+ restore_token_ = token;
+}
+
+std::string ScreenCastPortal::RestoreToken() const {
+ return restore_token_;
+}
+
+void ScreenCastPortal::OpenPipeWireRemote() {
+ GVariantBuilder builder;
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+
+ RTC_LOG(LS_INFO) << "Opening the PipeWire remote.";
+
+ g_dbus_proxy_call_with_unix_fd_list(
+ proxy_, "OpenPipeWireRemote",
+ g_variant_new("(oa{sv})", session_handle_.c_str(), &builder),
+ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, /*fd_list=*/nullptr, cancellable_,
+ reinterpret_cast<GAsyncReadyCallback>(OnOpenPipeWireRemoteRequested),
+ this);
+}
+
+// static
+void ScreenCastPortal::OnOpenPipeWireRemoteRequested(GDBusProxy* proxy,
+ GAsyncResult* result,
+ gpointer user_data) {
+ ScreenCastPortal* that = static_cast<ScreenCastPortal*>(user_data);
+ RTC_DCHECK(that);
+
+ Scoped<GError> error;
+ Scoped<GUnixFDList> outlist;
+ Scoped<GVariant> variant(g_dbus_proxy_call_with_unix_fd_list_finish(
+ proxy, outlist.receive(), result, error.receive()));
+ if (!variant) {
+ if (g_error_matches(error.get(), G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ return;
+ RTC_LOG(LS_ERROR) << "Failed to open the PipeWire remote: "
+ << error->message;
+ that->OnPortalDone(RequestResponse::kError);
+ return;
+ }
+
+ int32_t index;
+ g_variant_get(variant.get(), "(h)", &index);
+
+ that->pw_fd_ = g_unix_fd_list_get(outlist.get(), index, error.receive());
+
+ if (that->pw_fd_ == -1) {
+ RTC_LOG(LS_ERROR) << "Failed to get file descriptor from the list: "
+ << error->message;
+ that->OnPortalDone(RequestResponse::kError);
+ return;
+ }
+
+ that->OnPortalDone(RequestResponse::kSuccess);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_portal.h b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_portal.h
new file mode 100644
index 0000000000..7970710c41
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_portal.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_PORTAL_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_PORTAL_H_
+
+#include <gio/gio.h>
+
+#include <string>
+
+#include "modules/desktop_capture/linux/wayland/portal_request_response.h"
+#include "modules/desktop_capture/linux/wayland/screen_capture_portal_interface.h"
+#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
+#include "modules/desktop_capture/linux/wayland/xdg_session_details.h"
+
+namespace webrtc {
+
+class ScreenCastPortal : public xdg_portal::ScreenCapturePortalInterface {
+ public:
+ using ProxyRequestResponseHandler = void (*)(GObject* object,
+ GAsyncResult* result,
+ gpointer user_data);
+
+ using SourcesRequestResponseSignalHandler =
+ void (*)(GDBusConnection* connection,
+ const char* sender_name,
+ const char* object_path,
+ const char* interface_name,
+ const char* signal_name,
+ GVariant* parameters,
+ gpointer user_data);
+
+ // Values are set based on source type property in
+ // xdg-desktop-portal/screencast
+ // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml
+ enum class CaptureSourceType : uint32_t {
+ kScreen = 0b01,
+ kWindow = 0b10,
+ kAnyScreenContent = kScreen | kWindow
+ };
+
+ // Values are set based on cursor mode property in
+ // xdg-desktop-portal/screencast
+ // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml
+ enum class CursorMode : uint32_t {
+ // Mouse cursor will not be included in any form
+ kHidden = 0b01,
+ // Mouse cursor will be part of the screen content
+ kEmbedded = 0b10,
+ // Mouse cursor information will be send separately in form of metadata
+ kMetadata = 0b100
+ };
+
+ // Values are set based on persist mode property in
+ // xdg-desktop-portal/screencast
+ // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml
+ enum class PersistMode : uint32_t {
+ // Do not allow to restore stream
+ kDoNotPersist = 0b00,
+ // The restore token is valid as long as the application is alive. It's
+ // stored in memory and revoked when the application closes its DBus
+ // connection
+ kTransient = 0b01,
+ // The restore token is stored in disk and is valid until the user manually
+ // revokes it
+ kPersistent = 0b10
+ };
+
+ // Interface that must be implemented by the ScreenCastPortal consumers.
+ class PortalNotifier {
+ public:
+ virtual void OnScreenCastRequestResult(xdg_portal::RequestResponse result,
+ uint32_t stream_node_id,
+ int fd) = 0;
+ virtual void OnScreenCastSessionClosed() = 0;
+
+ protected:
+ PortalNotifier() = default;
+ virtual ~PortalNotifier() = default;
+ };
+
+ explicit ScreenCastPortal(ScreenCastPortal::CaptureSourceType source_type,
+ PortalNotifier* notifier);
+ explicit ScreenCastPortal(
+ CaptureSourceType source_type,
+ PortalNotifier* notifier,
+ ProxyRequestResponseHandler proxy_request_response_handler,
+ SourcesRequestResponseSignalHandler
+ sources_request_response_signal_handler,
+ gpointer user_data);
+ ~ScreenCastPortal();
+
+ // Initialize ScreenCastPortal with series of DBus calls where we try to
+ // obtain all the required information, like PipeWire file descriptor and
+ // PipeWire stream node ID.
+ //
+ // The observer will return whether the communication with xdg-desktop-portal
+ // was successful and only then you will be able to get all the required
+ // information in order to continue working with PipeWire.
+ void Start() override;
+ xdg_portal::SessionDetails GetSessionDetails() override;
+
+ // Method to notify the reason for failure of a portal request.
+ void OnPortalDone(xdg_portal::RequestResponse result) override;
+
+ // Sends a create session request to the portal.
+ void RequestSession(GDBusProxy* proxy) override;
+ void Cleanup();
+
+ // Set of methods leveraged by remote desktop portal to setup a common session
+ // with screen cast portal.
+ void SetSessionDetails(const xdg_portal::SessionDetails& session_details);
+ uint32_t pipewire_stream_node_id();
+ void SourcesRequest();
+ void OpenPipeWireRemote();
+
+ // ScreenCast specific methods for stream restoration
+ void SetPersistMode(ScreenCastPortal::PersistMode mode);
+ void SetRestoreToken(const std::string& token);
+ std::string RestoreToken() const;
+
+ private:
+ PortalNotifier* notifier_;
+
+ // A PipeWire stream ID of stream we will be connecting to
+ uint32_t pw_stream_node_id_ = 0;
+ // A file descriptor of PipeWire socket
+ int pw_fd_ = -1;
+ // Restore token that can be used to restore previous session
+ std::string restore_token_;
+
+ CaptureSourceType capture_source_type_ =
+ ScreenCastPortal::CaptureSourceType::kScreen;
+
+ CursorMode cursor_mode_ = ScreenCastPortal::CursorMode::kMetadata;
+
+ PersistMode persist_mode_ = ScreenCastPortal::PersistMode::kDoNotPersist;
+
+ ProxyRequestResponseHandler proxy_request_response_handler_;
+ SourcesRequestResponseSignalHandler sources_request_response_signal_handler_;
+ gpointer user_data_;
+
+ GDBusConnection* connection_ = nullptr;
+ GDBusProxy* proxy_ = nullptr;
+ GCancellable* cancellable_ = nullptr;
+ std::string portal_handle_;
+ std::string session_handle_;
+ std::string sources_handle_;
+ std::string start_handle_;
+ guint session_request_signal_id_ = 0;
+ guint sources_request_signal_id_ = 0;
+ guint start_request_signal_id_ = 0;
+ guint session_closed_signal_id_ = 0;
+
+ void UnsubscribeSignalHandlers();
+ static void OnProxyRequested(GObject* object,
+ GAsyncResult* result,
+ gpointer user_data);
+ static void OnSessionRequested(GDBusProxy* proxy,
+ GAsyncResult* result,
+ gpointer user_data);
+ static void OnSessionRequestResponseSignal(GDBusConnection* connection,
+ const char* sender_name,
+ const char* object_path,
+ const char* interface_name,
+ const char* signal_name,
+ GVariant* parameters,
+ gpointer user_data);
+ static void OnSessionClosedSignal(GDBusConnection* connection,
+ const char* sender_name,
+ const char* object_path,
+ const char* interface_name,
+ const char* signal_name,
+ GVariant* parameters,
+ gpointer user_data);
+ static void OnSourcesRequested(GDBusProxy* proxy,
+ GAsyncResult* result,
+ gpointer user_data);
+ static void OnSourcesRequestResponseSignal(GDBusConnection* connection,
+ const char* sender_name,
+ const char* object_path,
+ const char* interface_name,
+ const char* signal_name,
+ GVariant* parameters,
+ gpointer user_data);
+
+ void StartRequest();
+ static void OnStartRequested(GDBusProxy* proxy,
+ GAsyncResult* result,
+ gpointer user_data);
+ static void OnStartRequestResponseSignal(GDBusConnection* connection,
+ const char* sender_name,
+ const char* object_path,
+ const char* interface_name,
+ const char* signal_name,
+ GVariant* parameters,
+ gpointer user_data);
+
+ static void OnOpenPipeWireRemoteRequested(GDBusProxy* proxy,
+ GAsyncResult* result,
+ gpointer user_data);
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_PORTAL_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc
new file mode 100644
index 0000000000..dc0784791d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_stream_utils.cc
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/wayland/screencast_stream_utils.h"
+
+#include <libdrm/drm_fourcc.h>
+#include <pipewire/pipewire.h>
+#include <spa/param/video/format-utils.h>
+
+#include <string>
+
+#include "rtc_base/string_to_number.h"
+
+#if !PW_CHECK_VERSION(0, 3, 29)
+#define SPA_POD_PROP_FLAG_MANDATORY (1u << 3)
+#endif
+#if !PW_CHECK_VERSION(0, 3, 33)
+#define SPA_POD_PROP_FLAG_DONT_FIXATE (1u << 4)
+#endif
+
+namespace webrtc {
+
+PipeWireThreadLoopLock::PipeWireThreadLoopLock(pw_thread_loop* loop)
+ : loop_(loop) {
+ pw_thread_loop_lock(loop_);
+}
+
+PipeWireThreadLoopLock::~PipeWireThreadLoopLock() {
+ pw_thread_loop_unlock(loop_);
+}
+
+PipeWireVersion PipeWireVersion::Parse(const absl::string_view& version) {
+ std::vector<absl::string_view> parsed_version = rtc::split(version, '.');
+
+ if (parsed_version.size() != 3) {
+ return {};
+ }
+
+ absl::optional<int> major = rtc::StringToNumber<int>(parsed_version.at(0));
+ absl::optional<int> minor = rtc::StringToNumber<int>(parsed_version.at(1));
+ absl::optional<int> micro = rtc::StringToNumber<int>(parsed_version.at(2));
+
+ // Return invalid version if we failed to parse it
+ if (!major || !minor || !micro) {
+ return {};
+ }
+
+ return {major.value(), minor.value(), micro.value()};
+}
+
+bool PipeWireVersion::operator>=(const PipeWireVersion& other) {
+ if (!major && !minor && !micro) {
+ return false;
+ }
+
+ return std::tie(major, minor, micro) >=
+ std::tie(other.major, other.minor, other.micro);
+}
+
+bool PipeWireVersion::operator<=(const PipeWireVersion& other) {
+ if (!major && !minor && !micro) {
+ return false;
+ }
+
+ return std::tie(major, minor, micro) <=
+ std::tie(other.major, other.minor, other.micro);
+}
+
+spa_pod* BuildFormat(spa_pod_builder* builder,
+ uint32_t format,
+ const std::vector<uint64_t>& modifiers,
+ const struct spa_rectangle* resolution) {
+ spa_pod_frame frames[2];
+ spa_rectangle pw_min_screen_bounds = spa_rectangle{1, 1};
+ spa_rectangle pw_max_screen_bounds = spa_rectangle{UINT32_MAX, UINT32_MAX};
+
+ spa_pod_builder_push_object(builder, &frames[0], SPA_TYPE_OBJECT_Format,
+ SPA_PARAM_EnumFormat);
+ spa_pod_builder_add(builder, SPA_FORMAT_mediaType,
+ SPA_POD_Id(SPA_MEDIA_TYPE_video), 0);
+ spa_pod_builder_add(builder, SPA_FORMAT_mediaSubtype,
+ SPA_POD_Id(SPA_MEDIA_SUBTYPE_raw), 0);
+ spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_format, SPA_POD_Id(format), 0);
+
+ if (modifiers.size()) {
+ if (modifiers.size() == 1 && modifiers[0] == DRM_FORMAT_MOD_INVALID) {
+ spa_pod_builder_prop(builder, SPA_FORMAT_VIDEO_modifier,
+ SPA_POD_PROP_FLAG_MANDATORY);
+ spa_pod_builder_long(builder, modifiers[0]);
+ } else {
+ spa_pod_builder_prop(
+ builder, SPA_FORMAT_VIDEO_modifier,
+ SPA_POD_PROP_FLAG_MANDATORY | SPA_POD_PROP_FLAG_DONT_FIXATE);
+ spa_pod_builder_push_choice(builder, &frames[1], SPA_CHOICE_Enum, 0);
+
+ // modifiers from the array
+ bool first = true;
+ for (int64_t val : modifiers) {
+ spa_pod_builder_long(builder, val);
+ // Add the first modifier twice as the very first value is the default
+ // option
+ if (first) {
+ spa_pod_builder_long(builder, val);
+ first = false;
+ }
+ }
+ spa_pod_builder_pop(builder, &frames[1]);
+ }
+ }
+
+ if (resolution) {
+ spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size,
+ SPA_POD_Rectangle(resolution), 0);
+ } else {
+ spa_pod_builder_add(builder, SPA_FORMAT_VIDEO_size,
+ SPA_POD_CHOICE_RANGE_Rectangle(&pw_min_screen_bounds,
+ &pw_min_screen_bounds,
+ &pw_max_screen_bounds),
+ 0);
+ }
+
+ return static_cast<spa_pod*>(spa_pod_builder_pop(builder, &frames[0]));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_stream_utils.h b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_stream_utils.h
new file mode 100644
index 0000000000..70262c2e39
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/screencast_stream_utils.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_STREAM_UTILS_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_STREAM_UTILS_H_
+
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "rtc_base/string_encode.h"
+
+struct pw_thread_loop;
+struct spa_pod;
+struct spa_pod_builder;
+struct spa_rectangle;
+
+namespace webrtc {
+
+// Locks pw_thread_loop in the current scope
+class PipeWireThreadLoopLock {
+ public:
+ explicit PipeWireThreadLoopLock(pw_thread_loop* loop);
+ ~PipeWireThreadLoopLock();
+
+ private:
+ pw_thread_loop* const loop_;
+};
+
+struct PipeWireVersion {
+ static PipeWireVersion Parse(const absl::string_view& version);
+
+ // Returns whether current version is newer or same as required version
+ bool operator>=(const PipeWireVersion& other);
+ // Returns whether current version is older or same as required version
+ bool operator<=(const PipeWireVersion& other);
+
+ int major = 0;
+ int minor = 0;
+ int micro = 0;
+};
+
+// Returns a spa_pod used to build PipeWire stream format using given
+// arguments. Modifiers are optional value and when present they will be
+// used with SPA_POD_PROP_FLAG_MANDATORY and SPA_POD_PROP_FLAG_DONT_FIXATE
+// flags.
+spa_pod* BuildFormat(spa_pod_builder* builder,
+ uint32_t format,
+ const std::vector<uint64_t>& modifiers,
+ const struct spa_rectangle* resolution);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SCREENCAST_STREAM_UTILS_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
new file mode 100644
index 0000000000..5cbeaee9bf
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc
@@ -0,0 +1,870 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/wayland/shared_screencast_stream.h"
+
+#include <fcntl.h>
+#include <libdrm/drm_fourcc.h>
+#include <pipewire/pipewire.h>
+#include <spa/param/video/format-utils.h>
+#include <sys/mman.h>
+
+#include <vector>
+
+#include "absl/memory/memory.h"
+#include "modules/desktop_capture/linux/wayland/egl_dmabuf.h"
+#include "modules/desktop_capture/linux/wayland/screencast_stream_utils.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/sanitizer.h"
+#include "rtc_base/synchronization/mutex.h"
+
+#if defined(WEBRTC_DLOPEN_PIPEWIRE)
+#include "modules/desktop_capture/linux/wayland/pipewire_stubs.h"
+using modules_desktop_capture_linux_wayland::InitializeStubs;
+using modules_desktop_capture_linux_wayland::kModuleDrm;
+using modules_desktop_capture_linux_wayland::kModulePipewire;
+using modules_desktop_capture_linux_wayland::StubPathMap;
+#endif // defined(WEBRTC_DLOPEN_PIPEWIRE)
+
+namespace webrtc {
+
+const int kBytesPerPixel = 4;
+
+#if defined(WEBRTC_DLOPEN_PIPEWIRE)
+const char kPipeWireLib[] = "libpipewire-0.3.so.0";
+const char kDrmLib[] = "libdrm.so.2";
+#endif
+
+constexpr int kCursorBpp = 4;
+constexpr int CursorMetaSize(int w, int h) {
+ return (sizeof(struct spa_meta_cursor) + sizeof(struct spa_meta_bitmap) +
+ w * h * kCursorBpp);
+}
+
+constexpr PipeWireVersion kDmaBufMinVersion = {0, 3, 24};
+constexpr PipeWireVersion kDmaBufModifierMinVersion = {0, 3, 33};
+constexpr PipeWireVersion kDropSingleModifierMinVersion = {0, 3, 40};
+
+class ScopedBuf {
+ public:
+ ScopedBuf() {}
+ ScopedBuf(uint8_t* map, int map_size, int fd)
+ : map_(map), map_size_(map_size), fd_(fd) {}
+ ~ScopedBuf() {
+ if (map_ != MAP_FAILED) {
+ munmap(map_, map_size_);
+ }
+ }
+
+ explicit operator bool() { return map_ != MAP_FAILED; }
+
+ void initialize(uint8_t* map, int map_size, int fd) {
+ map_ = map;
+ map_size_ = map_size;
+ fd_ = fd;
+ }
+
+ uint8_t* get() { return map_; }
+
+ protected:
+ uint8_t* map_ = static_cast<uint8_t*>(MAP_FAILED);
+ int map_size_;
+ int fd_;
+};
+
+class SharedScreenCastStreamPrivate {
+ public:
+ SharedScreenCastStreamPrivate();
+ ~SharedScreenCastStreamPrivate();
+
+ bool StartScreenCastStream(uint32_t stream_node_id,
+ int fd,
+ uint32_t width = 0,
+ uint32_t height = 0);
+ void UpdateScreenCastStreamResolution(uint32_t width, uint32_t height);
+ void StopScreenCastStream();
+ std::unique_ptr<DesktopFrame> CaptureFrame();
+ std::unique_ptr<MouseCursor> CaptureCursor();
+ DesktopVector CaptureCursorPosition();
+
+ private:
+ uint32_t pw_stream_node_id_ = 0;
+
+ DesktopSize stream_size_ = {};
+ DesktopSize frame_size_;
+
+ webrtc::Mutex queue_lock_;
+ ScreenCaptureFrameQueue<SharedDesktopFrame> queue_
+ RTC_GUARDED_BY(&queue_lock_);
+ std::unique_ptr<MouseCursor> mouse_cursor_;
+ DesktopVector mouse_cursor_position_ = DesktopVector(-1, -1);
+
+ int64_t modifier_;
+ std::unique_ptr<EglDmaBuf> egl_dmabuf_;
+ // List of modifiers we query as supported by the graphics card/driver
+ std::vector<uint64_t> modifiers_;
+
+ // PipeWire types
+ struct pw_context* pw_context_ = nullptr;
+ struct pw_core* pw_core_ = nullptr;
+ struct pw_stream* pw_stream_ = nullptr;
+ struct pw_thread_loop* pw_main_loop_ = nullptr;
+ struct spa_source* renegotiate_ = nullptr;
+
+ spa_hook spa_core_listener_;
+ spa_hook spa_stream_listener_;
+
+ // A number used to verify all previous methods and the resulting
+ // events have been handled.
+ int server_version_sync_ = 0;
+ // Version of the running PipeWire server we communicate with
+ PipeWireVersion pw_server_version_;
+ // Version of the library used to run our code
+ PipeWireVersion pw_client_version_;
+
+ // Resolution parameters.
+ uint32_t width_ = 0;
+ uint32_t height_ = 0;
+ webrtc::Mutex resolution_lock_;
+ // Resolution changes are processed during buffer processing.
+ bool pending_resolution_change_ RTC_GUARDED_BY(&resolution_lock_) = false;
+
+ // event handlers
+ pw_core_events pw_core_events_ = {};
+ pw_stream_events pw_stream_events_ = {};
+
+ struct spa_video_info_raw spa_video_format_;
+
+ void ProcessBuffer(pw_buffer* buffer);
+ void ConvertRGBxToBGRx(uint8_t* frame, uint32_t size);
+
+ // PipeWire callbacks
+ static void OnCoreError(void* data,
+ uint32_t id,
+ int seq,
+ int res,
+ const char* message);
+ static void OnCoreDone(void* user_data, uint32_t id, int seq);
+ static void OnCoreInfo(void* user_data, const pw_core_info* info);
+ static void OnStreamParamChanged(void* data,
+ uint32_t id,
+ const struct spa_pod* format);
+ static void OnStreamStateChanged(void* data,
+ pw_stream_state old_state,
+ pw_stream_state state,
+ const char* error_message);
+ static void OnStreamProcess(void* data);
+ // This will be invoked in case we fail to process DMA-BUF PW buffer using
+ // negotiated stream parameters (modifier). We will drop the modifier we
+ // failed to use and try to use a different one or fallback to shared memory
+ // buffers.
+ static void OnRenegotiateFormat(void* data, uint64_t);
+};
+
+void SharedScreenCastStreamPrivate::OnCoreError(void* data,
+ uint32_t id,
+ int seq,
+ int res,
+ const char* message) {
+ SharedScreenCastStreamPrivate* that =
+ static_cast<SharedScreenCastStreamPrivate*>(data);
+ RTC_DCHECK(that);
+
+ RTC_LOG(LS_ERROR) << "PipeWire remote error: " << message;
+}
+
+void SharedScreenCastStreamPrivate::OnCoreInfo(void* data,
+ const pw_core_info* info) {
+ SharedScreenCastStreamPrivate* stream =
+ static_cast<SharedScreenCastStreamPrivate*>(data);
+ RTC_DCHECK(stream);
+
+ stream->pw_server_version_ = PipeWireVersion::Parse(info->version);
+}
+
+void SharedScreenCastStreamPrivate::OnCoreDone(void* data,
+ uint32_t id,
+ int seq) {
+ const SharedScreenCastStreamPrivate* stream =
+ static_cast<SharedScreenCastStreamPrivate*>(data);
+ RTC_DCHECK(stream);
+
+ if (id == PW_ID_CORE && stream->server_version_sync_ == seq) {
+ pw_thread_loop_signal(stream->pw_main_loop_, false);
+ }
+}
+
+// static
+void SharedScreenCastStreamPrivate::OnStreamStateChanged(
+ void* data,
+ pw_stream_state old_state,
+ pw_stream_state state,
+ const char* error_message) {
+ SharedScreenCastStreamPrivate* that =
+ static_cast<SharedScreenCastStreamPrivate*>(data);
+ RTC_DCHECK(that);
+
+ switch (state) {
+ case PW_STREAM_STATE_ERROR:
+ RTC_LOG(LS_ERROR) << "PipeWire stream state error: " << error_message;
+ break;
+ case PW_STREAM_STATE_PAUSED:
+ case PW_STREAM_STATE_STREAMING:
+ case PW_STREAM_STATE_UNCONNECTED:
+ case PW_STREAM_STATE_CONNECTING:
+ break;
+ }
+}
+
+// static
+void SharedScreenCastStreamPrivate::OnStreamParamChanged(
+ void* data,
+ uint32_t id,
+ const struct spa_pod* format) {
+ SharedScreenCastStreamPrivate* that =
+ static_cast<SharedScreenCastStreamPrivate*>(data);
+ RTC_DCHECK(that);
+
+ RTC_LOG(LS_INFO) << "PipeWire stream format changed.";
+ if (!format || id != SPA_PARAM_Format) {
+ return;
+ }
+
+ spa_format_video_raw_parse(format, &that->spa_video_format_);
+
+ auto width = that->spa_video_format_.size.width;
+ auto height = that->spa_video_format_.size.height;
+ auto stride = SPA_ROUND_UP_N(width * kBytesPerPixel, 4);
+ auto size = height * stride;
+
+ that->stream_size_ = DesktopSize(width, height);
+
+ uint8_t buffer[1024] = {};
+ auto builder = spa_pod_builder{buffer, sizeof(buffer)};
+
+ // Setup buffers and meta header for new format.
+
+ // When SPA_FORMAT_VIDEO_modifier is present we can use DMA-BUFs as
+ // the server announces support for it.
+ // See https://github.com/PipeWire/pipewire/blob/master/doc/dma-buf.dox
+ const bool has_modifier =
+ spa_pod_find_prop(format, nullptr, SPA_FORMAT_VIDEO_modifier);
+ that->modifier_ =
+ has_modifier ? that->spa_video_format_.modifier : DRM_FORMAT_MOD_INVALID;
+ std::vector<const spa_pod*> params;
+ const int buffer_types =
+ has_modifier || (that->pw_server_version_ >= kDmaBufMinVersion)
+ ? (1 << SPA_DATA_DmaBuf) | (1 << SPA_DATA_MemFd) |
+ (1 << SPA_DATA_MemPtr)
+ : (1 << SPA_DATA_MemFd) | (1 << SPA_DATA_MemPtr);
+
+ params.push_back(reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(
+ &builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers,
+ SPA_PARAM_BUFFERS_size, SPA_POD_Int(size), SPA_PARAM_BUFFERS_stride,
+ SPA_POD_Int(stride), SPA_PARAM_BUFFERS_buffers,
+ SPA_POD_CHOICE_RANGE_Int(8, 1, 32), SPA_PARAM_BUFFERS_dataType,
+ SPA_POD_CHOICE_FLAGS_Int(buffer_types))));
+ params.push_back(reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(
+ &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type,
+ SPA_POD_Id(SPA_META_Header), SPA_PARAM_META_size,
+ SPA_POD_Int(sizeof(struct spa_meta_header)))));
+ params.push_back(reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(
+ &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type,
+ SPA_POD_Id(SPA_META_VideoCrop), SPA_PARAM_META_size,
+ SPA_POD_Int(sizeof(struct spa_meta_region)))));
+ params.push_back(reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(
+ &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type,
+ SPA_POD_Id(SPA_META_Cursor), SPA_PARAM_META_size,
+ SPA_POD_CHOICE_RANGE_Int(CursorMetaSize(64, 64), CursorMetaSize(1, 1),
+ CursorMetaSize(384, 384)))));
+ params.push_back(reinterpret_cast<spa_pod*>(spa_pod_builder_add_object(
+ &builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type,
+ SPA_POD_Id(SPA_META_VideoDamage), SPA_PARAM_META_size,
+ SPA_POD_CHOICE_RANGE_Int(sizeof(struct spa_meta_region) * 16,
+ sizeof(struct spa_meta_region) * 1,
+ sizeof(struct spa_meta_region) * 16))));
+
+ pw_stream_update_params(that->pw_stream_, params.data(), params.size());
+}
+
+// static
+void SharedScreenCastStreamPrivate::OnStreamProcess(void* data) {
+ SharedScreenCastStreamPrivate* that =
+ static_cast<SharedScreenCastStreamPrivate*>(data);
+ RTC_DCHECK(that);
+
+ struct pw_buffer* next_buffer;
+ struct pw_buffer* buffer = nullptr;
+
+ next_buffer = pw_stream_dequeue_buffer(that->pw_stream_);
+ while (next_buffer) {
+ buffer = next_buffer;
+ next_buffer = pw_stream_dequeue_buffer(that->pw_stream_);
+
+ if (next_buffer) {
+ pw_stream_queue_buffer(that->pw_stream_, buffer);
+ }
+ }
+
+ if (!buffer) {
+ return;
+ }
+
+ that->ProcessBuffer(buffer);
+
+ pw_stream_queue_buffer(that->pw_stream_, buffer);
+}
+
+void SharedScreenCastStreamPrivate::OnRenegotiateFormat(void* data, uint64_t) {
+ SharedScreenCastStreamPrivate* that =
+ static_cast<SharedScreenCastStreamPrivate*>(data);
+ RTC_DCHECK(that);
+
+ {
+ PipeWireThreadLoopLock thread_loop_lock(that->pw_main_loop_);
+
+ uint8_t buffer[2048] = {};
+
+ spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)};
+
+ std::vector<const spa_pod*> params;
+ struct spa_rectangle resolution =
+ SPA_RECTANGLE(that->width_, that->height_);
+
+ webrtc::MutexLock lock(&that->resolution_lock_);
+ for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA,
+ SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) {
+ if (!that->modifiers_.empty()) {
+ params.push_back(BuildFormat(
+ &builder, format, that->modifiers_,
+ that->pending_resolution_change_ ? &resolution : nullptr));
+ }
+ params.push_back(BuildFormat(
+ &builder, format, /*modifiers=*/{},
+ that->pending_resolution_change_ ? &resolution : nullptr));
+ }
+
+ pw_stream_update_params(that->pw_stream_, params.data(), params.size());
+ that->pending_resolution_change_ = false;
+ }
+}
+
+SharedScreenCastStreamPrivate::SharedScreenCastStreamPrivate() {}
+
+SharedScreenCastStreamPrivate::~SharedScreenCastStreamPrivate() {
+ if (pw_main_loop_) {
+ pw_thread_loop_stop(pw_main_loop_);
+ }
+
+ if (pw_stream_) {
+ pw_stream_destroy(pw_stream_);
+ }
+
+ if (pw_core_) {
+ pw_core_disconnect(pw_core_);
+ }
+
+ if (pw_context_) {
+ pw_context_destroy(pw_context_);
+ }
+
+ if (pw_main_loop_) {
+ pw_thread_loop_destroy(pw_main_loop_);
+ }
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+bool SharedScreenCastStreamPrivate::StartScreenCastStream(
+ uint32_t stream_node_id,
+ int fd,
+ uint32_t width,
+ uint32_t height) {
+ width_ = width;
+ height_ = height;
+#if defined(WEBRTC_DLOPEN_PIPEWIRE)
+ StubPathMap paths;
+
+ // Check if the PipeWire and DRM libraries are available.
+ paths[kModulePipewire].push_back(kPipeWireLib);
+ paths[kModuleDrm].push_back(kDrmLib);
+
+ if (!InitializeStubs(paths)) {
+ RTC_LOG(LS_ERROR)
+ << "One of following libraries is missing on your system:\n"
+ << " - PipeWire (" << kPipeWireLib << ")\n"
+ << " - drm (" << kDrmLib << ")";
+ return false;
+ }
+#endif // defined(WEBRTC_DLOPEN_PIPEWIRE)
+ egl_dmabuf_ = std::make_unique<EglDmaBuf>();
+
+ pw_stream_node_id_ = stream_node_id;
+
+ pw_init(/*argc=*/nullptr, /*argc=*/nullptr);
+
+ pw_main_loop_ = pw_thread_loop_new("pipewire-main-loop", nullptr);
+
+ pw_context_ =
+ pw_context_new(pw_thread_loop_get_loop(pw_main_loop_), nullptr, 0);
+ if (!pw_context_) {
+ RTC_LOG(LS_ERROR) << "Failed to create PipeWire context";
+ return false;
+ }
+
+ if (pw_thread_loop_start(pw_main_loop_) < 0) {
+ RTC_LOG(LS_ERROR) << "Failed to start main PipeWire loop";
+ return false;
+ }
+
+ pw_client_version_ = PipeWireVersion::Parse(pw_get_library_version());
+
+ // Initialize event handlers, remote end and stream-related.
+ pw_core_events_.version = PW_VERSION_CORE_EVENTS;
+ pw_core_events_.info = &OnCoreInfo;
+ pw_core_events_.done = &OnCoreDone;
+ pw_core_events_.error = &OnCoreError;
+
+ pw_stream_events_.version = PW_VERSION_STREAM_EVENTS;
+ pw_stream_events_.state_changed = &OnStreamStateChanged;
+ pw_stream_events_.param_changed = &OnStreamParamChanged;
+ pw_stream_events_.process = &OnStreamProcess;
+
+ {
+ PipeWireThreadLoopLock thread_loop_lock(pw_main_loop_);
+
+ if (fd >= 0) {
+ pw_core_ = pw_context_connect_fd(
+ pw_context_, fcntl(fd, F_DUPFD_CLOEXEC), nullptr, 0);
+ } else {
+ pw_core_ = pw_context_connect(pw_context_, nullptr, 0);
+ }
+
+ if (!pw_core_) {
+ RTC_LOG(LS_ERROR) << "Failed to connect PipeWire context";
+ return false;
+ }
+
+ pw_core_add_listener(pw_core_, &spa_core_listener_, &pw_core_events_, this);
+
+ // Add an event that can be later invoked by pw_loop_signal_event()
+ renegotiate_ = pw_loop_add_event(pw_thread_loop_get_loop(pw_main_loop_),
+ OnRenegotiateFormat, this);
+
+ server_version_sync_ =
+ pw_core_sync(pw_core_, PW_ID_CORE, server_version_sync_);
+
+ pw_thread_loop_wait(pw_main_loop_);
+
+ pw_properties* reuseProps =
+ pw_properties_new_string("pipewire.client.reuse=1");
+ pw_stream_ = pw_stream_new(pw_core_, "webrtc-consume-stream", reuseProps);
+
+ if (!pw_stream_) {
+ RTC_LOG(LS_ERROR) << "Failed to create PipeWire stream";
+ return false;
+ }
+
+ pw_stream_add_listener(pw_stream_, &spa_stream_listener_,
+ &pw_stream_events_, this);
+ uint8_t buffer[2048] = {};
+
+ spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)};
+
+ std::vector<const spa_pod*> params;
+ const bool has_required_pw_client_version =
+ pw_client_version_ >= kDmaBufModifierMinVersion;
+ const bool has_required_pw_server_version =
+ pw_server_version_ >= kDmaBufModifierMinVersion;
+ struct spa_rectangle resolution;
+ bool set_resolution = false;
+ if (width && height) {
+ resolution = SPA_RECTANGLE(width, height);
+ set_resolution = true;
+ }
+ for (uint32_t format : {SPA_VIDEO_FORMAT_BGRA, SPA_VIDEO_FORMAT_RGBA,
+ SPA_VIDEO_FORMAT_BGRx, SPA_VIDEO_FORMAT_RGBx}) {
+ // Modifiers can be used with PipeWire >= 0.3.33
+ if (has_required_pw_client_version && has_required_pw_server_version) {
+ modifiers_ = egl_dmabuf_->QueryDmaBufModifiers(format);
+
+ if (!modifiers_.empty()) {
+ params.push_back(BuildFormat(&builder, format, modifiers_,
+ set_resolution ? &resolution : nullptr));
+ }
+ }
+
+ params.push_back(BuildFormat(&builder, format, /*modifiers=*/{},
+ set_resolution ? &resolution : nullptr));
+ }
+
+ if (pw_stream_connect(pw_stream_, PW_DIRECTION_INPUT, pw_stream_node_id_,
+ PW_STREAM_FLAG_AUTOCONNECT, params.data(),
+ params.size()) != 0) {
+ RTC_LOG(LS_ERROR) << "Could not connect receiving stream.";
+ return false;
+ }
+
+ RTC_LOG(LS_INFO) << "PipeWire remote opened.";
+ }
+ return true;
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+void SharedScreenCastStreamPrivate::UpdateScreenCastStreamResolution(
+ uint32_t width,
+ uint32_t height) {
+ if (!width || !height) {
+ RTC_LOG(LS_WARNING) << "Bad resolution specified: " << width << "x"
+ << height;
+ return;
+ }
+ if (!pw_main_loop_) {
+ RTC_LOG(LS_WARNING) << "No main pipewire loop, ignoring resolution change";
+ return;
+ }
+ if (!renegotiate_) {
+ RTC_LOG(LS_WARNING) << "Can not renegotiate stream params, ignoring "
+ << "resolution change";
+ return;
+ }
+ if (width_ != width || height_ != height) {
+ width_ = width;
+ height_ = height;
+ {
+ webrtc::MutexLock lock(&resolution_lock_);
+ pending_resolution_change_ = true;
+ }
+ pw_loop_signal_event(pw_thread_loop_get_loop(pw_main_loop_), renegotiate_);
+ }
+}
+
+void SharedScreenCastStreamPrivate::StopScreenCastStream() {
+ if (pw_stream_) {
+ pw_stream_disconnect(pw_stream_);
+ }
+}
+
+std::unique_ptr<DesktopFrame> SharedScreenCastStreamPrivate::CaptureFrame() {
+ webrtc::MutexLock lock(&queue_lock_);
+
+ if (!queue_.current_frame()) {
+ return std::unique_ptr<DesktopFrame>{};
+ }
+
+ std::unique_ptr<SharedDesktopFrame> frame = queue_.current_frame()->Share();
+ return std::move(frame);
+}
+
+std::unique_ptr<MouseCursor> SharedScreenCastStreamPrivate::CaptureCursor() {
+ if (!mouse_cursor_) {
+ return nullptr;
+ }
+
+ return std::move(mouse_cursor_);
+}
+
+DesktopVector SharedScreenCastStreamPrivate::CaptureCursorPosition() {
+ return mouse_cursor_position_;
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+void SharedScreenCastStreamPrivate::ProcessBuffer(pw_buffer* buffer) {
+ spa_buffer* spa_buffer = buffer->buffer;
+ ScopedBuf map;
+ std::unique_ptr<uint8_t[]> src_unique_ptr;
+ uint8_t* src = nullptr;
+
+ // Try to update the mouse cursor first, because it can be the only
+ // information carried by the buffer
+ {
+ const struct spa_meta_cursor* cursor =
+ static_cast<struct spa_meta_cursor*>(spa_buffer_find_meta_data(
+ spa_buffer, SPA_META_Cursor, sizeof(*cursor)));
+ if (cursor && spa_meta_cursor_is_valid(cursor)) {
+ struct spa_meta_bitmap* bitmap = nullptr;
+
+ if (cursor->bitmap_offset)
+ bitmap =
+ SPA_MEMBER(cursor, cursor->bitmap_offset, struct spa_meta_bitmap);
+
+ if (bitmap && bitmap->size.width > 0 && bitmap->size.height > 0) {
+ const uint8_t* bitmap_data =
+ SPA_MEMBER(bitmap, bitmap->offset, uint8_t);
+ BasicDesktopFrame* mouse_frame = new BasicDesktopFrame(
+ DesktopSize(bitmap->size.width, bitmap->size.height));
+ mouse_frame->CopyPixelsFrom(
+ bitmap_data, bitmap->stride,
+ DesktopRect::MakeWH(bitmap->size.width, bitmap->size.height));
+ mouse_cursor_ = std::make_unique<MouseCursor>(
+ mouse_frame, DesktopVector(cursor->hotspot.x, cursor->hotspot.y));
+ }
+ mouse_cursor_position_.set(cursor->position.x, cursor->position.y);
+ }
+ }
+
+ if (spa_buffer->datas[0].chunk->size == 0) {
+ return;
+ }
+
+ if (spa_buffer->datas[0].type == SPA_DATA_MemFd) {
+ map.initialize(
+ static_cast<uint8_t*>(
+ mmap(nullptr,
+ spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset,
+ PROT_READ, MAP_PRIVATE, spa_buffer->datas[0].fd, 0)),
+ spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset,
+ spa_buffer->datas[0].fd);
+
+ if (!map) {
+ RTC_LOG(LS_ERROR) << "Failed to mmap the memory: "
+ << std::strerror(errno);
+ return;
+ }
+
+ src = SPA_MEMBER(map.get(), spa_buffer->datas[0].mapoffset, uint8_t);
+ } else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf) {
+ const uint n_planes = spa_buffer->n_datas;
+
+ if (!n_planes) {
+ return;
+ }
+
+ std::vector<EglDmaBuf::PlaneData> plane_datas;
+ for (uint32_t i = 0; i < n_planes; ++i) {
+ EglDmaBuf::PlaneData data = {
+ static_cast<int32_t>(spa_buffer->datas[i].fd),
+ static_cast<uint32_t>(spa_buffer->datas[i].chunk->stride),
+ static_cast<uint32_t>(spa_buffer->datas[i].chunk->offset)};
+ plane_datas.push_back(data);
+ }
+
+ // When importing DMA-BUFs, we use the stride (number of bytes from one row
+ // of pixels in the buffer) provided by PipeWire. The stride from PipeWire
+ // is given by the graphics driver and some drivers might add some
+ // additional padding for memory layout optimizations so not everytime the
+ // stride is equal to BYTES_PER_PIXEL x WIDTH. This is fine, because during
+ // the import we will use OpenGL and same graphics driver so it will be able
+ // to work with the stride it provided, but later on when we work with
+ // images we get from DMA-BUFs we will need to update the stride to be equal
+ // to BYTES_PER_PIXEL x WIDTH as that's the size of the DesktopFrame we
+ // allocate for each captured frame.
+ src_unique_ptr = egl_dmabuf_->ImageFromDmaBuf(
+ stream_size_, spa_video_format_.format, plane_datas, modifier_);
+ if (src_unique_ptr) {
+ src = src_unique_ptr.get();
+ } else {
+ RTC_LOG(LS_ERROR) << "Dropping DMA-BUF modifier: " << modifier_
+ << " and trying to renegotiate stream parameters";
+
+ if (pw_server_version_ >= kDropSingleModifierMinVersion) {
+ modifiers_.erase(
+ std::remove(modifiers_.begin(), modifiers_.end(), modifier_),
+ modifiers_.end());
+ } else {
+ modifiers_.clear();
+ }
+
+ pw_loop_signal_event(pw_thread_loop_get_loop(pw_main_loop_),
+ renegotiate_);
+ return;
+ }
+ } else if (spa_buffer->datas[0].type == SPA_DATA_MemPtr) {
+ src = static_cast<uint8_t*>(spa_buffer->datas[0].data);
+ }
+
+ if (!src) {
+ return;
+ }
+
+ // Use SPA_META_VideoCrop metadata to get the frame size. KDE and GNOME do
+ // handle screen/window sharing differently. KDE/KWin doesn't use
+ // SPA_META_VideoCrop metadata and when sharing a window, it always sets
+ // stream size to size of the window. With that we just allocate the
+ // DesktopFrame using the size of the stream itself. GNOME/Mutter
+ // always sets stream size to the size of the whole screen, even when sharing
+ // a window. To get the real window size we have to use SPA_META_VideoCrop
+ // metadata. This gives us the size we need in order to allocate the
+ // DesktopFrame.
+
+ struct spa_meta_region* videocrop_metadata =
+ static_cast<struct spa_meta_region*>(spa_buffer_find_meta_data(
+ spa_buffer, SPA_META_VideoCrop, sizeof(*videocrop_metadata)));
+
+ // Video size from metadata is bigger than an actual video stream size.
+ // The metadata are wrong or we should up-scale the video...in both cases
+ // just quit now.
+ if (videocrop_metadata &&
+ (videocrop_metadata->region.size.width >
+ static_cast<uint32_t>(stream_size_.width()) ||
+ videocrop_metadata->region.size.height >
+ static_cast<uint32_t>(stream_size_.height()))) {
+ RTC_LOG(LS_ERROR) << "Stream metadata sizes are wrong!";
+ return;
+ }
+
+ // Use SPA_META_VideoCrop metadata to get the DesktopFrame size in case
+ // a windows is shared and it represents just a small portion of the
+ // stream itself. This will be for example used in case of GNOME (Mutter)
+ // where the stream will have the size of the screen itself, but we care
+ // only about smaller portion representing the window inside.
+ bool videocrop_metadata_use = false;
+ const struct spa_rectangle* videocrop_metadata_size =
+ videocrop_metadata ? &videocrop_metadata->region.size : nullptr;
+
+ if (videocrop_metadata_size && videocrop_metadata_size->width != 0 &&
+ videocrop_metadata_size->height != 0 &&
+ (static_cast<int>(videocrop_metadata_size->width) <
+ stream_size_.width() ||
+ static_cast<int>(videocrop_metadata_size->height) <
+ stream_size_.height())) {
+ videocrop_metadata_use = true;
+ }
+
+ if (videocrop_metadata_use) {
+ frame_size_ = DesktopSize(videocrop_metadata_size->width,
+ videocrop_metadata_size->height);
+ } else {
+ frame_size_ = stream_size_;
+ }
+
+ // Get the position of the video crop within the stream. Just double-check
+ // that the position doesn't exceed the size of the stream itself. NOTE:
+ // Currently it looks there is no implementation using this.
+ uint32_t y_offset =
+ videocrop_metadata_use &&
+ (videocrop_metadata->region.position.y + frame_size_.height() <=
+ stream_size_.height())
+ ? videocrop_metadata->region.position.y
+ : 0;
+ uint32_t x_offset =
+ videocrop_metadata_use &&
+ (videocrop_metadata->region.position.x + frame_size_.width() <=
+ stream_size_.width())
+ ? videocrop_metadata->region.position.x
+ : 0;
+
+ const uint32_t stream_stride = kBytesPerPixel * stream_size_.width();
+ uint32_t buffer_stride = spa_buffer->datas[0].chunk->stride;
+ uint32_t src_stride = buffer_stride;
+
+ if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf &&
+ buffer_stride > stream_stride) {
+ // When DMA-BUFs are used, sometimes spa_buffer->stride we get might
+ // contain additional padding, but after we import the buffer, the stride
+ // we used is no longer relevant and we should just calculate it based on
+ // the stream width. For more context see https://crbug.com/1333304.
+ src_stride = stream_stride;
+ }
+
+ uint8_t* updated_src =
+ src + (src_stride * y_offset) + (kBytesPerPixel * x_offset);
+
+ webrtc::MutexLock lock(&queue_lock_);
+
+ // Move to the next frame if the current one is being used and shared
+ if (queue_.current_frame() && queue_.current_frame()->IsShared()) {
+ queue_.MoveToNextFrame();
+ if (queue_.current_frame() && queue_.current_frame()->IsShared()) {
+ RTC_LOG(LS_WARNING)
+ << "Failed to process PipeWire buffer: no available frame";
+ return;
+ }
+ }
+
+ if (!queue_.current_frame() ||
+ !queue_.current_frame()->size().equals(frame_size_)) {
+ std::unique_ptr<DesktopFrame> frame(new BasicDesktopFrame(
+ DesktopSize(frame_size_.width(), frame_size_.height())));
+ queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(frame)));
+ }
+
+ queue_.current_frame()->CopyPixelsFrom(
+ updated_src, (src_stride - (kBytesPerPixel * x_offset)),
+ DesktopRect::MakeWH(frame_size_.width(), frame_size_.height()));
+
+ if (spa_video_format_.format == SPA_VIDEO_FORMAT_RGBx ||
+ spa_video_format_.format == SPA_VIDEO_FORMAT_RGBA) {
+ uint8_t* tmp_src = queue_.current_frame()->data();
+ for (int i = 0; i < frame_size_.height(); ++i) {
+ // If both sides decided to go with the RGBx format we need to convert
+ // it to BGRx to match color format expected by WebRTC.
+ ConvertRGBxToBGRx(tmp_src, queue_.current_frame()->stride());
+ tmp_src += queue_.current_frame()->stride();
+ }
+ }
+
+ queue_.current_frame()->mutable_updated_region()->SetRect(
+ DesktopRect::MakeSize(queue_.current_frame()->size()));
+}
+
+void SharedScreenCastStreamPrivate::ConvertRGBxToBGRx(uint8_t* frame,
+ uint32_t size) {
+ for (uint32_t i = 0; i < size; i += 4) {
+ uint8_t tempR = frame[i];
+ uint8_t tempB = frame[i + 2];
+ frame[i] = tempB;
+ frame[i + 2] = tempR;
+ }
+}
+
+SharedScreenCastStream::SharedScreenCastStream()
+ : private_(std::make_unique<SharedScreenCastStreamPrivate>()) {}
+
+SharedScreenCastStream::~SharedScreenCastStream() {}
+
+rtc::scoped_refptr<SharedScreenCastStream>
+SharedScreenCastStream::CreateDefault() {
+ // Explicit new, to access non-public constructor.
+ return rtc::scoped_refptr(new SharedScreenCastStream());
+}
+
+bool SharedScreenCastStream::StartScreenCastStream(uint32_t stream_node_id) {
+ return private_->StartScreenCastStream(stream_node_id, -1);
+}
+
+bool SharedScreenCastStream::StartScreenCastStream(uint32_t stream_node_id,
+ int fd,
+ uint32_t width,
+ uint32_t height) {
+ return private_->StartScreenCastStream(stream_node_id, fd, width, height);
+}
+
+void SharedScreenCastStream::UpdateScreenCastStreamResolution(uint32_t width,
+ uint32_t height) {
+ private_->UpdateScreenCastStreamResolution(width, height);
+}
+
+void SharedScreenCastStream::StopScreenCastStream() {
+ private_->StopScreenCastStream();
+}
+
+std::unique_ptr<DesktopFrame> SharedScreenCastStream::CaptureFrame() {
+ return private_->CaptureFrame();
+}
+
+std::unique_ptr<MouseCursor> SharedScreenCastStream::CaptureCursor() {
+ return private_->CaptureCursor();
+}
+
+absl::optional<DesktopVector> SharedScreenCastStream::CaptureCursorPosition() {
+ DesktopVector position = private_->CaptureCursorPosition();
+
+ // Consider only (x >= 0 and y >= 0) a valid position
+ if (position.x() < 0 || position.y() < 0) {
+ return absl::nullopt;
+ }
+
+ return position;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.h b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.h
new file mode 100644
index 0000000000..66a3f45bdb
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/shared_screencast_stream.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SHARED_SCREENCAST_STREAM_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SHARED_SCREENCAST_STREAM_H_
+
+#include <memory>
+
+#include "absl/types/optional.h"
+#include "api/ref_counted_base.h"
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+class SharedScreenCastStreamPrivate;
+
+class RTC_EXPORT SharedScreenCastStream
+ : public rtc::RefCountedNonVirtual<SharedScreenCastStream> {
+ public:
+ static rtc::scoped_refptr<SharedScreenCastStream> CreateDefault();
+
+ bool StartScreenCastStream(uint32_t stream_node_id);
+ bool StartScreenCastStream(uint32_t stream_node_id,
+ int fd,
+ uint32_t width = 0,
+ uint32_t height = 0);
+ void UpdateScreenCastStreamResolution(uint32_t width, uint32_t height);
+ void StopScreenCastStream();
+
+ // Below functions return the most recent information we get from a
+ // PipeWire buffer on each Process() callback. This assumes that we
+ // managed to successfuly connect to a PipeWire stream provided by the
+ // compositor (based on stream parameters). The cursor data are obtained
+ // from spa_meta_cursor stream metadata and therefore the cursor is not
+ // part of actual screen/window frame.
+
+ // Returns the most recent screen/window frame we obtained from PipeWire
+ // buffer. Will return an empty frame in case we didn't manage to get a frame
+ // from PipeWire buffer.
+ std::unique_ptr<DesktopFrame> CaptureFrame();
+
+ // Returns the most recent mouse cursor image. Will return an nullptr cursor
+ // in case we didn't manage to get a cursor from PipeWire buffer. NOTE: the
+ // cursor image might not be updated on every cursor location change, but
+ // actually only when its shape changes.
+ std::unique_ptr<MouseCursor> CaptureCursor();
+
+ // Returns the most recent mouse cursor position. Will not return a value in
+ // case we didn't manage to get it from PipeWire buffer.
+ absl::optional<DesktopVector> CaptureCursorPosition();
+
+ ~SharedScreenCastStream();
+
+ protected:
+ SharedScreenCastStream();
+
+ private:
+ SharedScreenCastStream(const SharedScreenCastStream&) = delete;
+ SharedScreenCastStream& operator=(const SharedScreenCastStream&) = delete;
+
+ std::unique_ptr<SharedScreenCastStreamPrivate> private_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_SHARED_SCREENCAST_STREAM_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.cc b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.cc
new file mode 100644
index 0000000000..75dbf2bdf3
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.cc
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#include "modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h"
+
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "modules/desktop_capture/linux/wayland/scoped_glib.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace xdg_portal {
+
+std::string RequestResponseToString(RequestResponse request) {
+ switch (request) {
+ case RequestResponse::kUnknown:
+ return "kUnknown";
+ case RequestResponse::kSuccess:
+ return "kSuccess";
+ case RequestResponse::kUserCancelled:
+ return "kUserCancelled";
+ case RequestResponse::kError:
+ return "kError";
+ default:
+ return "Uknown";
+ }
+}
+
+RequestResponse RequestResponseFromPortalResponse(uint32_t portal_response) {
+ // See:
+ // https://docs.flatpak.org/en/latest/portal-api-reference.html#gdbus-signal-org-freedesktop-portal-Request.Response
+ switch (portal_response) {
+ case 0:
+ return RequestResponse::kSuccess;
+ case 1:
+ return RequestResponse::kUserCancelled;
+ case 2:
+ return RequestResponse::kError;
+ default:
+ return RequestResponse::kUnknown;
+ }
+}
+
+std::string PrepareSignalHandle(absl::string_view token,
+ GDBusConnection* connection) {
+ Scoped<char> sender(
+ g_strdup(g_dbus_connection_get_unique_name(connection) + 1));
+ for (int i = 0; sender.get()[i]; ++i) {
+ if (sender.get()[i] == '.') {
+ sender.get()[i] = '_';
+ }
+ }
+ const char* handle =
+ g_strconcat(kDesktopRequestObjectPath, "/", sender.get(), "/",
+ std::string(token).c_str(), /*end of varargs*/ nullptr);
+ return handle;
+}
+
+uint32_t SetupRequestResponseSignal(absl::string_view object_path,
+ const GDBusSignalCallback callback,
+ gpointer user_data,
+ GDBusConnection* connection) {
+ return g_dbus_connection_signal_subscribe(
+ connection, kDesktopBusName, kRequestInterfaceName, "Response",
+ std::string(object_path).c_str(), /*arg0=*/nullptr,
+ G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, callback, user_data,
+ /*user_data_free_func=*/nullptr);
+}
+
+void RequestSessionProxy(absl::string_view interface_name,
+ const ProxyRequestCallback proxy_request_callback,
+ GCancellable* cancellable,
+ gpointer user_data) {
+ g_dbus_proxy_new_for_bus(
+ G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr,
+ kDesktopBusName, kDesktopObjectPath, std::string(interface_name).c_str(),
+ cancellable,
+ reinterpret_cast<GAsyncReadyCallback>(proxy_request_callback), user_data);
+}
+
+void SetupSessionRequestHandlers(
+ absl::string_view portal_prefix,
+ const SessionRequestCallback session_request_callback,
+ const SessionRequestResponseSignalHandler request_response_signale_handler,
+ GDBusConnection* connection,
+ GDBusProxy* proxy,
+ GCancellable* cancellable,
+ std::string& portal_handle,
+ guint& session_request_signal_id,
+ gpointer user_data) {
+ GVariantBuilder builder;
+ Scoped<char> variant_string;
+
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+ variant_string =
+ g_strdup_printf("%.*s_session%d", static_cast<int>(portal_prefix.size()),
+ portal_prefix.data(), g_random_int_range(0, G_MAXINT));
+ g_variant_builder_add(&builder, "{sv}", "session_handle_token",
+ g_variant_new_string(variant_string.get()));
+
+ variant_string =
+ g_strdup_printf("%.*s_%d", static_cast<int>(portal_prefix.size()),
+ portal_prefix.data(), g_random_int_range(0, G_MAXINT));
+ g_variant_builder_add(&builder, "{sv}", "handle_token",
+ g_variant_new_string(variant_string.get()));
+
+ portal_handle = PrepareSignalHandle(variant_string.get(), connection);
+ session_request_signal_id = SetupRequestResponseSignal(
+ portal_handle.c_str(), request_response_signale_handler, user_data,
+ connection);
+
+ RTC_LOG(LS_INFO) << "Desktop session requested.";
+ g_dbus_proxy_call(
+ proxy, "CreateSession", g_variant_new("(a{sv})", &builder),
+ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable,
+ reinterpret_cast<GAsyncReadyCallback>(session_request_callback),
+ user_data);
+}
+
+void StartSessionRequest(
+ absl::string_view prefix,
+ absl::string_view session_handle,
+ const StartRequestResponseSignalHandler signal_handler,
+ const SessionStartRequestedHandler session_started_handler,
+ GDBusProxy* proxy,
+ GDBusConnection* connection,
+ GCancellable* cancellable,
+ guint& start_request_signal_id,
+ std::string& start_handle,
+ gpointer user_data) {
+ GVariantBuilder builder;
+ Scoped<char> variant_string;
+
+ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT);
+ variant_string =
+ g_strdup_printf("%.*s%d", static_cast<int>(prefix.size()), prefix.data(),
+ g_random_int_range(0, G_MAXINT));
+ g_variant_builder_add(&builder, "{sv}", "handle_token",
+ g_variant_new_string(variant_string.get()));
+
+ start_handle = PrepareSignalHandle(variant_string.get(), connection);
+ start_request_signal_id = SetupRequestResponseSignal(
+ start_handle.c_str(), signal_handler, user_data, connection);
+
+ // "Identifier for the application window", this is Wayland, so not "x11:...".
+ const char parent_window[] = "";
+
+ RTC_LOG(LS_INFO) << "Starting the portal session.";
+ g_dbus_proxy_call(
+ proxy, "Start",
+ g_variant_new("(osa{sv})", std::string(session_handle).c_str(),
+ parent_window, &builder),
+ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, cancellable,
+ reinterpret_cast<GAsyncReadyCallback>(session_started_handler),
+ user_data);
+}
+
+void TearDownSession(absl::string_view session_handle,
+ GDBusProxy* proxy,
+ GCancellable* cancellable,
+ GDBusConnection* connection) {
+ if (!session_handle.empty()) {
+ Scoped<GDBusMessage> message(g_dbus_message_new_method_call(
+ kDesktopBusName, std::string(session_handle).c_str(),
+ kSessionInterfaceName, "Close"));
+ if (message.get()) {
+ Scoped<GError> error;
+ g_dbus_connection_send_message(connection, message.get(),
+ G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+ /*out_serial=*/nullptr, error.receive());
+ if (error.get()) {
+ RTC_LOG(LS_ERROR) << "Failed to close the session: " << error->message;
+ }
+ }
+ }
+
+ if (cancellable) {
+ g_cancellable_cancel(cancellable);
+ g_object_unref(cancellable);
+ }
+
+ if (proxy) {
+ g_object_unref(proxy);
+ }
+}
+
+} // namespace xdg_portal
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h
new file mode 100644
index 0000000000..f6ac92b5d1
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/xdg_desktop_portal_utils.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_DESKTOP_PORTAL_UTILS_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_DESKTOP_PORTAL_UTILS_H_
+
+#include <gio/gio.h>
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "modules/desktop_capture/linux/wayland/portal_request_response.h"
+#include "modules/desktop_capture/linux/wayland/scoped_glib.h"
+#include "modules/desktop_capture/linux/wayland/xdg_session_details.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace xdg_portal {
+
+constexpr char kDesktopBusName[] = "org.freedesktop.portal.Desktop";
+constexpr char kDesktopObjectPath[] = "/org/freedesktop/portal/desktop";
+constexpr char kDesktopRequestObjectPath[] =
+ "/org/freedesktop/portal/desktop/request";
+constexpr char kSessionInterfaceName[] = "org.freedesktop.portal.Session";
+constexpr char kRequestInterfaceName[] = "org.freedesktop.portal.Request";
+constexpr char kScreenCastInterfaceName[] = "org.freedesktop.portal.ScreenCast";
+
+using ProxyRequestCallback = void (*)(GObject*, GAsyncResult*, gpointer);
+using SessionRequestCallback = void (*)(GDBusProxy*, GAsyncResult*, gpointer);
+using SessionRequestResponseSignalHandler = void (*)(GDBusConnection*,
+ const char*,
+ const char*,
+ const char*,
+ const char*,
+ GVariant*,
+ gpointer);
+using StartRequestResponseSignalHandler = void (*)(GDBusConnection*,
+ const char*,
+ const char*,
+ const char*,
+ const char*,
+ GVariant*,
+ gpointer);
+using SessionStartRequestedHandler = void (*)(GDBusProxy*,
+ GAsyncResult*,
+ gpointer);
+
+std::string RequestResponseToString(RequestResponse request);
+
+RequestResponse RequestResponseFromPortalResponse(uint32_t portal_response);
+
+// Returns a string path for signal handle based on the provided connection and
+// token.
+std::string PrepareSignalHandle(absl::string_view token,
+ GDBusConnection* connection);
+
+// Sets up the callback to execute when a response signal is received for the
+// given object.
+uint32_t SetupRequestResponseSignal(absl::string_view object_path,
+ const GDBusSignalCallback callback,
+ gpointer user_data,
+ GDBusConnection* connection);
+
+void RequestSessionProxy(absl::string_view interface_name,
+ const ProxyRequestCallback proxy_request_callback,
+ GCancellable* cancellable,
+ gpointer user_data);
+
+void SetupSessionRequestHandlers(
+ absl::string_view portal_prefix,
+ const SessionRequestCallback session_request_callback,
+ const SessionRequestResponseSignalHandler request_response_signale_handler,
+ GDBusConnection* connection,
+ GDBusProxy* proxy,
+ GCancellable* cancellable,
+ std::string& portal_handle,
+ guint& session_request_signal_id,
+ gpointer user_data);
+
+void StartSessionRequest(
+ absl::string_view prefix,
+ absl::string_view session_handle,
+ const StartRequestResponseSignalHandler signal_handler,
+ const SessionStartRequestedHandler session_started_handler,
+ GDBusProxy* proxy,
+ GDBusConnection* connection,
+ GCancellable* cancellable,
+ guint& start_request_signal_id,
+ std::string& start_handle,
+ gpointer user_data);
+
+// Tears down the portal session and cleans up related objects.
+void TearDownSession(absl::string_view session_handle,
+ GDBusProxy* proxy,
+ GCancellable* cancellable,
+ GDBusConnection* connection);
+
+} // namespace xdg_portal
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_DESKTOP_PORTAL_UTILS_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/wayland/xdg_session_details.h b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/xdg_session_details.h
new file mode 100644
index 0000000000..b70ac4aa59
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/wayland/xdg_session_details.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_SESSION_DETAILS_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_SESSION_DETAILS_H_
+
+#include <gio/gio.h>
+
+#include <string>
+
+namespace webrtc {
+namespace xdg_portal {
+
+// Details of the session associated with XDG desktop portal session. Portal API
+// calls can be invoked by utilizing the information here.
+struct SessionDetails {
+ GDBusProxy* proxy = nullptr;
+ GCancellable* cancellable = nullptr;
+ std::string session_handle;
+ uint32_t pipewire_stream_node_id = 0;
+};
+
+} // namespace xdg_portal
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_WAYLAND_XDG_SESSION_DETAILS_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc
new file mode 100644
index 0000000000..12f554df79
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.cc
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h"
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xfixes.h>
+#include <X11/extensions/xfixeswire.h>
+#include <stddef.h>
+#include <stdint.h>
+
+#include <algorithm>
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/linux/x11/x_error_trap.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/mouse_cursor_monitor.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace {
+
+// WindowCapturer returns window IDs of X11 windows with WM_STATE attribute.
+// These windows may not be immediate children of the root window, because
+// window managers may re-parent them to add decorations. However,
+// XQueryPointer() expects to be passed children of the root. This function
+// searches up the list of the windows to find the root child that corresponds
+// to `window`.
+Window GetTopLevelWindow(Display* display, Window window) {
+ webrtc::XErrorTrap error_trap(display);
+ while (true) {
+ // If the window is in WithdrawnState then look at all of its children.
+ ::Window root, parent;
+ ::Window* children;
+ unsigned int num_children;
+ if (!XQueryTree(display, window, &root, &parent, &children,
+ &num_children)) {
+ RTC_LOG(LS_ERROR) << "Failed to query for child windows although window"
+ "does not have a valid WM_STATE.";
+ return None;
+ }
+ if (children)
+ XFree(children);
+
+ if (parent == root)
+ break;
+
+ window = parent;
+ }
+
+ return window;
+}
+
+} // namespace
+
+namespace webrtc {
+
+MouseCursorMonitorX11::MouseCursorMonitorX11(
+ const DesktopCaptureOptions& options,
+ Window window)
+ : x_display_(options.x_display()),
+ callback_(NULL),
+ mode_(SHAPE_AND_POSITION),
+ window_(window),
+ have_xfixes_(false),
+ xfixes_event_base_(-1),
+ xfixes_error_base_(-1) {
+ // Set a default initial cursor shape in case XFixes is not present.
+ const int kSize = 5;
+ std::unique_ptr<DesktopFrame> default_cursor(
+ new BasicDesktopFrame(DesktopSize(kSize, kSize)));
+ const uint8_t pixels[kSize * kSize] = {
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff,
+ 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ uint8_t* ptr = default_cursor->data();
+ for (int y = 0; y < kSize; ++y) {
+ for (int x = 0; x < kSize; ++x) {
+ *ptr++ = pixels[kSize * y + x];
+ *ptr++ = pixels[kSize * y + x];
+ *ptr++ = pixels[kSize * y + x];
+ *ptr++ = 0xff;
+ }
+ }
+ DesktopVector hotspot(2, 2);
+ cursor_shape_.reset(new MouseCursor(default_cursor.release(), hotspot));
+}
+
+MouseCursorMonitorX11::~MouseCursorMonitorX11() {
+ if (have_xfixes_) {
+ x_display_->RemoveEventHandler(xfixes_event_base_ + XFixesCursorNotify,
+ this);
+ }
+}
+
+void MouseCursorMonitorX11::Init(Callback* callback, Mode mode) {
+ // Init can be called only if not started
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+
+ callback_ = callback;
+ mode_ = mode;
+
+ have_xfixes_ =
+ XFixesQueryExtension(display(), &xfixes_event_base_, &xfixes_error_base_);
+
+ if (have_xfixes_) {
+ // Register for changes to the cursor shape.
+ XErrorTrap error_trap(display());
+ XFixesSelectCursorInput(display(), window_, XFixesDisplayCursorNotifyMask);
+ x_display_->AddEventHandler(xfixes_event_base_ + XFixesCursorNotify, this);
+
+ CaptureCursor();
+ } else {
+ RTC_LOG(LS_INFO) << "X server does not support XFixes.";
+ }
+}
+
+void MouseCursorMonitorX11::Capture() {
+ RTC_DCHECK(callback_);
+
+ // Process X11 events in case XFixes has sent cursor notification.
+ x_display_->ProcessPendingXEvents();
+
+ // cursor_shape_| is set only if we were notified of a cursor shape change.
+ if (cursor_shape_.get())
+ callback_->OnMouseCursor(cursor_shape_.release());
+
+ // Get cursor position if necessary.
+ if (mode_ == SHAPE_AND_POSITION) {
+ int root_x;
+ int root_y;
+ int win_x;
+ int win_y;
+ Window root_window;
+ Window child_window;
+ unsigned int mask;
+
+ XErrorTrap error_trap(display());
+ Bool result = XQueryPointer(display(), window_, &root_window, &child_window,
+ &root_x, &root_y, &win_x, &win_y, &mask);
+ CursorState state;
+ if (!result || error_trap.GetLastErrorAndDisable() != 0) {
+ state = OUTSIDE;
+ } else {
+ // In screen mode (window_ == root_window) the mouse is always inside.
+ // XQueryPointer() sets `child_window` to None if the cursor is outside
+ // `window_`.
+ state =
+ (window_ == root_window || child_window != None) ? INSIDE : OUTSIDE;
+ }
+
+ // As the comments to GetTopLevelWindow() above indicate, in window capture,
+ // the cursor position capture happens in `window_`, while the frame catpure
+ // happens in `child_window`. These two windows are not alwyas same, as
+ // window manager may add some decorations to the `window_`. So translate
+ // the coordinate in `window_` to the coordinate space of `child_window`.
+ if (window_ != root_window && state == INSIDE) {
+ int translated_x, translated_y;
+ Window unused;
+ if (XTranslateCoordinates(display(), window_, child_window, win_x, win_y,
+ &translated_x, &translated_y, &unused)) {
+ win_x = translated_x;
+ win_y = translated_y;
+ }
+ }
+
+ // X11 always starts the coordinate from (0, 0), so we do not need to
+ // translate here.
+ callback_->OnMouseCursorPosition(DesktopVector(root_x, root_y));
+ }
+}
+
+bool MouseCursorMonitorX11::HandleXEvent(const XEvent& event) {
+ if (have_xfixes_ && event.type == xfixes_event_base_ + XFixesCursorNotify) {
+ const XFixesCursorNotifyEvent* cursor_event =
+ reinterpret_cast<const XFixesCursorNotifyEvent*>(&event);
+ if (cursor_event->subtype == XFixesDisplayCursorNotify) {
+ CaptureCursor();
+ }
+ // Return false, even if the event has been handled, because there might be
+ // other listeners for cursor notifications.
+ }
+ return false;
+}
+
+void MouseCursorMonitorX11::CaptureCursor() {
+ RTC_DCHECK(have_xfixes_);
+
+ XFixesCursorImage* img;
+ {
+ XErrorTrap error_trap(display());
+ img = XFixesGetCursorImage(display());
+ if (!img || error_trap.GetLastErrorAndDisable() != 0)
+ return;
+ }
+
+ std::unique_ptr<DesktopFrame> image(
+ new BasicDesktopFrame(DesktopSize(img->width, img->height)));
+
+ // Xlib stores 32-bit data in longs, even if longs are 64-bits long.
+ unsigned long* src = img->pixels; // NOLINT(runtime/int)
+ uint32_t* dst = reinterpret_cast<uint32_t*>(image->data());
+ uint32_t* dst_end = dst + (img->width * img->height);
+ while (dst < dst_end) {
+ *dst++ = static_cast<uint32_t>(*src++);
+ }
+
+ DesktopVector hotspot(std::min(img->width, img->xhot),
+ std::min(img->height, img->yhot));
+
+ XFree(img);
+
+ cursor_shape_.reset(new MouseCursor(image.release(), hotspot));
+}
+
+// static
+MouseCursorMonitor* MouseCursorMonitorX11::CreateForWindow(
+ const DesktopCaptureOptions& options,
+ WindowId window) {
+ if (!options.x_display())
+ return NULL;
+ window = GetTopLevelWindow(options.x_display()->display(), window);
+ if (window == None)
+ return NULL;
+ return new MouseCursorMonitorX11(options, window);
+}
+
+MouseCursorMonitor* MouseCursorMonitorX11::CreateForScreen(
+ const DesktopCaptureOptions& options,
+ ScreenId screen) {
+ if (!options.x_display())
+ return NULL;
+ WindowId window = DefaultRootWindow(options.x_display()->display());
+ return new MouseCursorMonitorX11(options, window);
+}
+
+std::unique_ptr<MouseCursorMonitor> MouseCursorMonitorX11::Create(
+ const DesktopCaptureOptions& options) {
+ return std::unique_ptr<MouseCursorMonitor>(
+ CreateForScreen(options, kFullDesktopScreenId));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h
new file mode 100644
index 0000000000..980d254a0a
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_MOUSE_CURSOR_MONITOR_X11_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_MOUSE_CURSOR_MONITOR_X11_H_
+
+#include <X11/X.h>
+
+#include <memory>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/linux/x11/shared_x_display.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/mouse_cursor_monitor.h"
+
+namespace webrtc {
+
+class MouseCursorMonitorX11 : public MouseCursorMonitor,
+ public SharedXDisplay::XEventHandler {
+ public:
+ MouseCursorMonitorX11(const DesktopCaptureOptions& options, Window window);
+ ~MouseCursorMonitorX11() override;
+
+ static MouseCursorMonitor* CreateForWindow(
+ const DesktopCaptureOptions& options,
+ WindowId window);
+ static MouseCursorMonitor* CreateForScreen(
+ const DesktopCaptureOptions& options,
+ ScreenId screen);
+ static std::unique_ptr<MouseCursorMonitor> Create(
+ const DesktopCaptureOptions& options);
+
+ void Init(Callback* callback, Mode mode) override;
+ void Capture() override;
+
+ private:
+ // SharedXDisplay::XEventHandler interface.
+ bool HandleXEvent(const XEvent& event) override;
+
+ Display* display() { return x_display_->display(); }
+
+ // Captures current cursor shape and stores it in `cursor_shape_`.
+ void CaptureCursor();
+
+ rtc::scoped_refptr<SharedXDisplay> x_display_;
+ Callback* callback_;
+ Mode mode_;
+ Window window_;
+
+ bool have_xfixes_;
+ int xfixes_event_base_;
+ int xfixes_error_base_;
+
+ std::unique_ptr<MouseCursor> cursor_shape_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_MOUSE_CURSOR_MONITOR_X11_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.cc
new file mode 100644
index 0000000000..684838bee5
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.cc
@@ -0,0 +1,512 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/screen_capturer_x11.h"
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xfixes.h>
+#include <X11/extensions/damagewire.h>
+#include <dlfcn.h>
+#include <stdint.h>
+#include <string.h>
+
+#include <memory>
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/linux/x11/x_server_pixel_buffer.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/screen_capturer_helper.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/sanitizer.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/trace_event.h"
+
+namespace webrtc {
+
+ScreenCapturerX11::ScreenCapturerX11() {
+ helper_.SetLogGridSize(4);
+}
+
+ScreenCapturerX11::~ScreenCapturerX11() {
+ options_.x_display()->RemoveEventHandler(ConfigureNotify, this);
+ if (use_damage_) {
+ options_.x_display()->RemoveEventHandler(damage_event_base_ + XDamageNotify,
+ this);
+ }
+ if (use_randr_) {
+ options_.x_display()->RemoveEventHandler(
+ randr_event_base_ + RRScreenChangeNotify, this);
+ }
+ DeinitXlib();
+}
+
+bool ScreenCapturerX11::Init(const DesktopCaptureOptions& options) {
+ TRACE_EVENT0("webrtc", "ScreenCapturerX11::Init");
+ options_ = options;
+
+ atom_cache_ = std::make_unique<XAtomCache>(display());
+
+ root_window_ = RootWindow(display(), DefaultScreen(display()));
+ if (root_window_ == BadValue) {
+ RTC_LOG(LS_ERROR) << "Unable to get the root window";
+ DeinitXlib();
+ return false;
+ }
+
+ gc_ = XCreateGC(display(), root_window_, 0, NULL);
+ if (gc_ == NULL) {
+ RTC_LOG(LS_ERROR) << "Unable to get graphics context";
+ DeinitXlib();
+ return false;
+ }
+
+ options_.x_display()->AddEventHandler(ConfigureNotify, this);
+
+ // Check for XFixes extension. This is required for cursor shape
+ // notifications, and for our use of XDamage.
+ if (XFixesQueryExtension(display(), &xfixes_event_base_,
+ &xfixes_error_base_)) {
+ has_xfixes_ = true;
+ } else {
+ RTC_LOG(LS_INFO) << "X server does not support XFixes.";
+ }
+
+ // Register for changes to the dimensions of the root window.
+ XSelectInput(display(), root_window_, StructureNotifyMask);
+
+ if (!x_server_pixel_buffer_.Init(atom_cache_.get(),
+ DefaultRootWindow(display()))) {
+ RTC_LOG(LS_ERROR) << "Failed to initialize pixel buffer.";
+ return false;
+ }
+
+ if (options_.use_update_notifications()) {
+ InitXDamage();
+ }
+
+ InitXrandr();
+
+ // Default source set here so that selected_monitor_rect_ is sized correctly.
+ SelectSource(kFullDesktopScreenId);
+
+ return true;
+}
+
+void ScreenCapturerX11::InitXDamage() {
+ // Our use of XDamage requires XFixes.
+ if (!has_xfixes_) {
+ return;
+ }
+
+ // Check for XDamage extension.
+ if (!XDamageQueryExtension(display(), &damage_event_base_,
+ &damage_error_base_)) {
+ RTC_LOG(LS_INFO) << "X server does not support XDamage.";
+ return;
+ }
+
+ // TODO(lambroslambrou): Disable DAMAGE in situations where it is known
+ // to fail, such as when Desktop Effects are enabled, with graphics
+ // drivers (nVidia, ATI) that fail to report DAMAGE notifications
+ // properly.
+
+ // Request notifications every time the screen becomes damaged.
+ damage_handle_ =
+ XDamageCreate(display(), root_window_, XDamageReportNonEmpty);
+ if (!damage_handle_) {
+ RTC_LOG(LS_ERROR) << "Unable to initialize XDamage.";
+ return;
+ }
+
+ // Create an XFixes server-side region to collate damage into.
+ damage_region_ = XFixesCreateRegion(display(), 0, 0);
+ if (!damage_region_) {
+ XDamageDestroy(display(), damage_handle_);
+ RTC_LOG(LS_ERROR) << "Unable to create XFixes region.";
+ return;
+ }
+
+ options_.x_display()->AddEventHandler(damage_event_base_ + XDamageNotify,
+ this);
+
+ use_damage_ = true;
+ RTC_LOG(LS_INFO) << "Using XDamage extension.";
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+void ScreenCapturerX11::InitXrandr() {
+ int major_version = 0;
+ int minor_version = 0;
+ int error_base_ignored = 0;
+ if (XRRQueryExtension(display(), &randr_event_base_, &error_base_ignored) &&
+ XRRQueryVersion(display(), &major_version, &minor_version)) {
+ if (major_version > 1 || (major_version == 1 && minor_version >= 5)) {
+ // Dynamically link XRRGetMonitors and XRRFreeMonitors as a workaround
+ // to avoid a dependency issue with Debian 8.
+ get_monitors_ = reinterpret_cast<get_monitors_func>(
+ dlsym(RTLD_DEFAULT, "XRRGetMonitors"));
+ free_monitors_ = reinterpret_cast<free_monitors_func>(
+ dlsym(RTLD_DEFAULT, "XRRFreeMonitors"));
+ if (get_monitors_ && free_monitors_) {
+ use_randr_ = true;
+ RTC_LOG(LS_INFO) << "Using XRandR extension v" << major_version << '.'
+ << minor_version << '.';
+ monitors_ =
+ get_monitors_(display(), root_window_, true, &num_monitors_);
+
+ // Register for screen change notifications
+ XRRSelectInput(display(), root_window_, RRScreenChangeNotifyMask);
+ options_.x_display()->AddEventHandler(
+ randr_event_base_ + RRScreenChangeNotify, this);
+ } else {
+ RTC_LOG(LS_ERROR) << "Unable to link XRandR monitor functions.";
+ }
+ } else {
+ RTC_LOG(LS_ERROR) << "XRandR entension is older than v1.5.";
+ }
+ } else {
+ RTC_LOG(LS_ERROR) << "X server does not support XRandR.";
+ }
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+void ScreenCapturerX11::UpdateMonitors() {
+ if (monitors_) {
+ free_monitors_(monitors_);
+ monitors_ = nullptr;
+ }
+
+ monitors_ = get_monitors_(display(), root_window_, true, &num_monitors_);
+
+ if (selected_monitor_name_) {
+ if (selected_monitor_name_ == static_cast<Atom>(kFullDesktopScreenId)) {
+ selected_monitor_rect_ =
+ DesktopRect::MakeSize(x_server_pixel_buffer_.window_size());
+ return;
+ }
+
+ for (int i = 0; i < num_monitors_; ++i) {
+ XRRMonitorInfo& m = monitors_[i];
+ if (selected_monitor_name_ == m.name) {
+ RTC_LOG(LS_INFO) << "XRandR monitor " << m.name << " rect updated.";
+ selected_monitor_rect_ =
+ DesktopRect::MakeXYWH(m.x, m.y, m.width, m.height);
+ const auto& pixel_buffer_rect = x_server_pixel_buffer_.window_rect();
+ if (!pixel_buffer_rect.ContainsRect(selected_monitor_rect_)) {
+ // This is never expected to happen, but crop the rectangle anyway
+ // just in case the server returns inconsistent information.
+ // CaptureScreen() expects `selected_monitor_rect_` to lie within
+ // the pixel-buffer's rectangle.
+ RTC_LOG(LS_WARNING)
+ << "Cropping selected monitor rect to fit the pixel-buffer.";
+ selected_monitor_rect_.IntersectWith(pixel_buffer_rect);
+ }
+ return;
+ }
+ }
+
+ // The selected monitor is not connected anymore
+ RTC_LOG(LS_INFO) << "XRandR selected monitor " << selected_monitor_name_
+ << " lost.";
+ selected_monitor_rect_ = DesktopRect::MakeWH(0, 0);
+ }
+}
+
+void ScreenCapturerX11::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+
+ callback_ = callback;
+}
+
+void ScreenCapturerX11::CaptureFrame() {
+ TRACE_EVENT0("webrtc", "ScreenCapturerX11::CaptureFrame");
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ queue_.MoveToNextFrame();
+ if (queue_.current_frame() && queue_.current_frame()->IsShared()) {
+ RTC_DLOG(LS_WARNING) << "Overwriting frame that is still shared.";
+ }
+
+ // Process XEvents for XDamage and cursor shape tracking.
+ options_.x_display()->ProcessPendingXEvents();
+
+ // ProcessPendingXEvents() may call ScreenConfigurationChanged() which
+ // reinitializes `x_server_pixel_buffer_`. Check if the pixel buffer is still
+ // in a good shape.
+ if (!x_server_pixel_buffer_.is_initialized()) {
+ // We failed to initialize pixel buffer.
+ RTC_LOG(LS_ERROR) << "Pixel buffer is not initialized.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+
+ // Allocate the current frame buffer only if it is not already allocated.
+ // Note that we can't reallocate other buffers at this point, since the caller
+ // may still be reading from them.
+ if (!queue_.current_frame()) {
+ std::unique_ptr<DesktopFrame> frame(
+ new BasicDesktopFrame(selected_monitor_rect_.size()));
+
+ // We set the top-left of the frame so the mouse cursor will be composited
+ // properly, and our frame buffer will not be overrun while blitting.
+ frame->set_top_left(selected_monitor_rect_.top_left());
+ queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(frame)));
+ }
+
+ std::unique_ptr<DesktopFrame> result = CaptureScreen();
+ if (!result) {
+ RTC_LOG(LS_WARNING) << "Temporarily failed to capture screen.";
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+
+ last_invalid_region_ = result->updated_region();
+ result->set_capture_time_ms((rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec);
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(result));
+}
+
+bool ScreenCapturerX11::GetSourceList(SourceList* sources) {
+ RTC_DCHECK(sources->size() == 0);
+ if (!use_randr_) {
+ sources->push_back({});
+ return true;
+ }
+
+ // Ensure that `monitors_` is updated with changes that may have happened
+ // between calls to GetSourceList().
+ options_.x_display()->ProcessPendingXEvents();
+
+ for (int i = 0; i < num_monitors_; ++i) {
+ XRRMonitorInfo& m = monitors_[i];
+ char* monitor_title = XGetAtomName(display(), m.name);
+
+ // Note name is an X11 Atom used to id the monitor.
+ sources->push_back({static_cast<SourceId>(m.name), 0, monitor_title});
+ XFree(monitor_title);
+ }
+
+ return true;
+}
+
+bool ScreenCapturerX11::SelectSource(SourceId id) {
+ // Prevent the reuse of any frame buffers allocated for a previously selected
+ // source. This is required to stop crashes, or old data from appearing in
+ // a captured frame, when the new source is sized differently then the source
+ // that was selected at the time a reused frame buffer was created.
+ queue_.Reset();
+
+ if (!use_randr_ || id == kFullDesktopScreenId) {
+ selected_monitor_name_ = kFullDesktopScreenId;
+ selected_monitor_rect_ =
+ DesktopRect::MakeSize(x_server_pixel_buffer_.window_size());
+ return true;
+ }
+
+ for (int i = 0; i < num_monitors_; ++i) {
+ if (id == static_cast<SourceId>(monitors_[i].name)) {
+ RTC_LOG(LS_INFO) << "XRandR selected source: " << id;
+ XRRMonitorInfo& m = monitors_[i];
+ selected_monitor_name_ = m.name;
+ selected_monitor_rect_ =
+ DesktopRect::MakeXYWH(m.x, m.y, m.width, m.height);
+ const auto& pixel_buffer_rect = x_server_pixel_buffer_.window_rect();
+ if (!pixel_buffer_rect.ContainsRect(selected_monitor_rect_)) {
+ RTC_LOG(LS_WARNING)
+ << "Cropping selected monitor rect to fit the pixel-buffer.";
+ selected_monitor_rect_.IntersectWith(pixel_buffer_rect);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ScreenCapturerX11::HandleXEvent(const XEvent& event) {
+ if (use_damage_ && (event.type == damage_event_base_ + XDamageNotify)) {
+ const XDamageNotifyEvent* damage_event =
+ reinterpret_cast<const XDamageNotifyEvent*>(&event);
+ if (damage_event->damage != damage_handle_)
+ return false;
+ RTC_DCHECK(damage_event->level == XDamageReportNonEmpty);
+ return true;
+ } else if (use_randr_ &&
+ event.type == randr_event_base_ + RRScreenChangeNotify) {
+ XRRUpdateConfiguration(const_cast<XEvent*>(&event));
+ UpdateMonitors();
+ RTC_LOG(LS_INFO) << "XRandR screen change event received.";
+ return false;
+ } else if (event.type == ConfigureNotify) {
+ ScreenConfigurationChanged();
+ return false;
+ }
+ return false;
+}
+
+std::unique_ptr<DesktopFrame> ScreenCapturerX11::CaptureScreen() {
+ std::unique_ptr<SharedDesktopFrame> frame = queue_.current_frame()->Share();
+ RTC_DCHECK(selected_monitor_rect_.size().equals(frame->size()));
+ RTC_DCHECK(selected_monitor_rect_.top_left().equals(frame->top_left()));
+
+ // Pass the screen size to the helper, so it can clip the invalid region if it
+ // expands that region to a grid. Note that the helper operates in the
+ // DesktopFrame coordinate system where the top-left pixel is (0, 0), even for
+ // a monitor with non-zero offset relative to `x_server_pixel_buffer_`.
+ helper_.set_size_most_recent(frame->size());
+
+ // In the DAMAGE case, ensure the frame is up-to-date with the previous frame
+ // if any. If there isn't a previous frame, that means a screen-resolution
+ // change occurred, and `invalid_rects` will be updated to include the whole
+ // screen.
+ if (use_damage_ && queue_.previous_frame())
+ SynchronizeFrame();
+
+ DesktopRegion* updated_region = frame->mutable_updated_region();
+
+ x_server_pixel_buffer_.Synchronize();
+ if (use_damage_ && queue_.previous_frame()) {
+ // Atomically fetch and clear the damage region.
+ XDamageSubtract(display(), damage_handle_, None, damage_region_);
+ int rects_num = 0;
+ XRectangle bounds;
+ XRectangle* rects = XFixesFetchRegionAndBounds(display(), damage_region_,
+ &rects_num, &bounds);
+ for (int i = 0; i < rects_num; ++i) {
+ auto damage_rect = DesktopRect::MakeXYWH(rects[i].x, rects[i].y,
+ rects[i].width, rects[i].height);
+
+ // Damage-regions are relative to `x_server_pixel_buffer`, so convert the
+ // region to DesktopFrame coordinates where the top-left is always (0, 0),
+ // before adding to the frame's updated_region. `helper_` also operates in
+ // DesktopFrame coordinates, and it will take care of cropping away any
+ // damage-regions that lie outside the selected monitor.
+ damage_rect.Translate(-frame->top_left());
+ updated_region->AddRect(damage_rect);
+ }
+ XFree(rects);
+ helper_.InvalidateRegion(*updated_region);
+
+ // Capture the damaged portions of the desktop.
+ helper_.TakeInvalidRegion(updated_region);
+
+ for (DesktopRegion::Iterator it(*updated_region); !it.IsAtEnd();
+ it.Advance()) {
+ auto rect = it.rect();
+ rect.Translate(frame->top_left());
+ if (!x_server_pixel_buffer_.CaptureRect(rect, frame.get()))
+ return nullptr;
+ }
+ } else {
+ // Doing full-screen polling, or this is the first capture after a
+ // screen-resolution change. In either case, need a full-screen capture.
+ if (!x_server_pixel_buffer_.CaptureRect(selected_monitor_rect_,
+ frame.get())) {
+ return nullptr;
+ }
+ updated_region->SetRect(DesktopRect::MakeSize(frame->size()));
+ }
+
+ return std::move(frame);
+}
+
+void ScreenCapturerX11::ScreenConfigurationChanged() {
+ TRACE_EVENT0("webrtc", "ScreenCapturerX11::ScreenConfigurationChanged");
+ // Make sure the frame buffers will be reallocated.
+ queue_.Reset();
+
+ helper_.ClearInvalidRegion();
+ if (!x_server_pixel_buffer_.Init(atom_cache_.get(),
+ DefaultRootWindow(display()))) {
+ RTC_LOG(LS_ERROR) << "Failed to initialize pixel buffer after screen "
+ "configuration change.";
+ }
+
+ if (use_randr_) {
+ // Adding/removing RANDR monitors can generate a ConfigureNotify event
+ // without generating any RRScreenChangeNotify event. So it is important to
+ // update the monitors here even if the screen resolution hasn't changed.
+ UpdateMonitors();
+ } else {
+ selected_monitor_rect_ =
+ DesktopRect::MakeSize(x_server_pixel_buffer_.window_size());
+ }
+}
+
+void ScreenCapturerX11::SynchronizeFrame() {
+ // Synchronize the current buffer with the previous one since we do not
+ // capture the entire desktop. Note that encoder may be reading from the
+ // previous buffer at this time so thread access complaints are false
+ // positives.
+
+ // TODO(hclam): We can reduce the amount of copying here by subtracting
+ // `capturer_helper_`s region from `last_invalid_region_`.
+ // http://crbug.com/92354
+ RTC_DCHECK(queue_.previous_frame());
+
+ DesktopFrame* current = queue_.current_frame();
+ DesktopFrame* last = queue_.previous_frame();
+ RTC_DCHECK(current != last);
+ for (DesktopRegion::Iterator it(last_invalid_region_); !it.IsAtEnd();
+ it.Advance()) {
+ const DesktopRect& r = it.rect();
+ current->CopyPixelsFrom(*last, r.top_left(), r);
+ }
+}
+
+RTC_NO_SANITIZE("cfi-icall")
+void ScreenCapturerX11::DeinitXlib() {
+ if (monitors_) {
+ free_monitors_(monitors_);
+ monitors_ = nullptr;
+ }
+
+ if (gc_) {
+ XFreeGC(display(), gc_);
+ gc_ = nullptr;
+ }
+
+ x_server_pixel_buffer_.Release();
+
+ if (display()) {
+ if (damage_handle_) {
+ XDamageDestroy(display(), damage_handle_);
+ damage_handle_ = 0;
+ }
+
+ if (damage_region_) {
+ XFixesDestroyRegion(display(), damage_region_);
+ damage_region_ = 0;
+ }
+ }
+}
+
+// static
+std::unique_ptr<DesktopCapturer> ScreenCapturerX11::CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options) {
+ if (!options.x_display())
+ return nullptr;
+
+ std::unique_ptr<ScreenCapturerX11> capturer(new ScreenCapturerX11());
+ if (!capturer.get()->Init(options)) {
+ return nullptr;
+ }
+
+ return std::move(capturer);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.h
new file mode 100644
index 0000000000..d2a437aaa2
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/screen_capturer_x11.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_SCREEN_CAPTURER_X11_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_SCREEN_CAPTURER_X11_H_
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/Xdamage.h>
+#include <X11/extensions/Xfixes.h>
+#include <X11/extensions/Xrandr.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/linux/x11/shared_x_display.h"
+#include "modules/desktop_capture/linux/x11/x_atom_cache.h"
+#include "modules/desktop_capture/linux/x11/x_server_pixel_buffer.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/screen_capturer_helper.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+
+namespace webrtc {
+
+// A class to perform video frame capturing for Linux on X11.
+//
+// If XDamage is used, this class sets DesktopFrame::updated_region() according
+// to the areas reported by XDamage. Otherwise this class does not detect
+// DesktopFrame::updated_region(), the field is always set to the entire frame
+// rectangle. ScreenCapturerDifferWrapper should be used if that functionality
+// is necessary.
+class ScreenCapturerX11 : public DesktopCapturer,
+ public SharedXDisplay::XEventHandler {
+ public:
+ ScreenCapturerX11();
+ ~ScreenCapturerX11() override;
+
+ ScreenCapturerX11(const ScreenCapturerX11&) = delete;
+ ScreenCapturerX11& operator=(const ScreenCapturerX11&) = delete;
+
+ static std::unique_ptr<DesktopCapturer> CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options);
+
+ // TODO(ajwong): Do we really want this to be synchronous?
+ bool Init(const DesktopCaptureOptions& options);
+
+ // DesktopCapturer interface.
+ void Start(Callback* delegate) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+
+ private:
+ Display* display() { return options_.x_display()->display(); }
+
+ // SharedXDisplay::XEventHandler interface.
+ bool HandleXEvent(const XEvent& event) override;
+
+ void InitXDamage();
+ void InitXrandr();
+ void UpdateMonitors();
+
+ // Capture screen pixels to the current buffer in the queue. In the DAMAGE
+ // case, the ScreenCapturerHelper already holds the list of invalid rectangles
+ // from HandleXEvent(). In the non-DAMAGE case, this captures the
+ // whole screen, then calculates some invalid rectangles that include any
+ // differences between this and the previous capture.
+ std::unique_ptr<DesktopFrame> CaptureScreen();
+
+ // Called when the screen configuration is changed.
+ void ScreenConfigurationChanged();
+
+ // Synchronize the current buffer with `last_buffer_`, by copying pixels from
+ // the area of `last_invalid_rects`.
+ // Note this only works on the assumption that kNumBuffers == 2, as
+ // `last_invalid_rects` holds the differences from the previous buffer and
+ // the one prior to that (which will then be the current buffer).
+ void SynchronizeFrame();
+
+ void DeinitXlib();
+
+ DesktopCaptureOptions options_;
+
+ Callback* callback_ = nullptr;
+
+ // X11 graphics context.
+ GC gc_ = nullptr;
+ Window root_window_ = BadValue;
+
+ // XRandR 1.5 monitors.
+ bool use_randr_ = false;
+ int randr_event_base_ = 0;
+ XRRMonitorInfo* monitors_ = nullptr;
+ int num_monitors_ = 0;
+ DesktopRect selected_monitor_rect_;
+ // selected_monitor_name_ will be changed to kFullDesktopScreenId
+ // by a call to SelectSource() at the end of Init() because
+ // selected_monitor_rect_ should be updated as well.
+ // Setting it to kFullDesktopScreenId here might be misleading.
+ Atom selected_monitor_name_ = 0;
+ typedef XRRMonitorInfo* (*get_monitors_func)(Display*, Window, Bool, int*);
+ typedef void (*free_monitors_func)(XRRMonitorInfo*);
+ get_monitors_func get_monitors_ = nullptr;
+ free_monitors_func free_monitors_ = nullptr;
+
+ // XFixes.
+ bool has_xfixes_ = false;
+ int xfixes_event_base_ = -1;
+ int xfixes_error_base_ = -1;
+
+ // XDamage information.
+ bool use_damage_ = false;
+ Damage damage_handle_ = 0;
+ int damage_event_base_ = -1;
+ int damage_error_base_ = -1;
+ XserverRegion damage_region_ = 0;
+
+ // Access to the X Server's pixel buffer.
+ XServerPixelBuffer x_server_pixel_buffer_;
+
+ // A thread-safe list of invalid rectangles, and the size of the most
+ // recently captured screen.
+ ScreenCapturerHelper helper_;
+
+ // Queue of the frames buffers.
+ ScreenCaptureFrameQueue<SharedDesktopFrame> queue_;
+
+ // Invalid region from the previous capture. This is used to synchronize the
+ // current with the last buffer used.
+ DesktopRegion last_invalid_region_;
+
+ std::unique_ptr<XAtomCache> atom_cache_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_SCREEN_CAPTURER_X11_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.cc
new file mode 100644
index 0000000000..b7849508b0
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.cc
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/shared_x_display.h"
+
+#include <X11/Xlib.h>
+#include <X11/extensions/XTest.h>
+
+#include <algorithm>
+
+#include "absl/strings/string_view.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+SharedXDisplay::SharedXDisplay(Display* display) : display_(display) {
+ RTC_DCHECK(display_);
+}
+
+SharedXDisplay::~SharedXDisplay() {
+ RTC_DCHECK(event_handlers_.empty());
+ XCloseDisplay(display_);
+}
+
+// static
+rtc::scoped_refptr<SharedXDisplay> SharedXDisplay::Create(
+ absl::string_view display_name) {
+ Display* display = XOpenDisplay(
+ display_name.empty() ? NULL : std::string(display_name).c_str());
+ if (!display) {
+ RTC_LOG(LS_ERROR) << "Unable to open display";
+ return nullptr;
+ }
+ return rtc::scoped_refptr<SharedXDisplay>(new SharedXDisplay(display));
+}
+
+// static
+rtc::scoped_refptr<SharedXDisplay> SharedXDisplay::CreateDefault() {
+ return Create(std::string());
+}
+
+void SharedXDisplay::AddEventHandler(int type, XEventHandler* handler) {
+ event_handlers_[type].push_back(handler);
+}
+
+void SharedXDisplay::RemoveEventHandler(int type, XEventHandler* handler) {
+ EventHandlersMap::iterator handlers = event_handlers_.find(type);
+ if (handlers == event_handlers_.end())
+ return;
+
+ std::vector<XEventHandler*>::iterator new_end =
+ std::remove(handlers->second.begin(), handlers->second.end(), handler);
+ handlers->second.erase(new_end, handlers->second.end());
+
+ // Check if no handlers left for this event.
+ if (handlers->second.empty())
+ event_handlers_.erase(handlers);
+}
+
+void SharedXDisplay::ProcessPendingXEvents() {
+ // Hold reference to `this` to prevent it from being destroyed while
+ // processing events.
+ rtc::scoped_refptr<SharedXDisplay> self(this);
+
+ // Find the number of events that are outstanding "now." We don't just loop
+ // on XPending because we want to guarantee this terminates.
+ int events_to_process = XPending(display());
+ XEvent e;
+
+ for (int i = 0; i < events_to_process; i++) {
+ XNextEvent(display(), &e);
+ EventHandlersMap::iterator handlers = event_handlers_.find(e.type);
+ if (handlers == event_handlers_.end())
+ continue;
+ for (std::vector<XEventHandler*>::iterator it = handlers->second.begin();
+ it != handlers->second.end(); ++it) {
+ if ((*it)->HandleXEvent(e))
+ break;
+ }
+ }
+}
+
+void SharedXDisplay::IgnoreXServerGrabs() {
+ int test_event_base = 0;
+ int test_error_base = 0;
+ int major = 0;
+ int minor = 0;
+ if (XTestQueryExtension(display(), &test_event_base, &test_error_base, &major,
+ &minor)) {
+ XTestGrabControl(display(), true);
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.h
new file mode 100644
index 0000000000..084da80167
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/shared_x_display.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_SHARED_X_DISPLAY_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_SHARED_X_DISPLAY_H_
+
+#include <map>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "api/ref_counted_base.h"
+#include "api/scoped_refptr.h"
+#include "rtc_base/system/rtc_export.h"
+
+// Including Xlib.h will involve evil defines (Bool, Status, True, False), which
+// easily conflict with other headers.
+typedef struct _XDisplay Display;
+typedef union _XEvent XEvent;
+
+namespace webrtc {
+
+// A ref-counted object to store XDisplay connection.
+class RTC_EXPORT SharedXDisplay
+ : public rtc::RefCountedNonVirtual<SharedXDisplay> {
+ public:
+ class XEventHandler {
+ public:
+ virtual ~XEventHandler() {}
+
+ // Processes XEvent. Returns true if the event has been handled.
+ virtual bool HandleXEvent(const XEvent& event) = 0;
+ };
+
+ // Creates a new X11 Display for the `display_name`. NULL is returned if X11
+ // connection failed. Equivalent to CreateDefault() when `display_name` is
+ // empty.
+ static rtc::scoped_refptr<SharedXDisplay> Create(
+ absl::string_view display_name);
+
+ // Creates X11 Display connection for the default display (e.g. specified in
+ // DISPLAY). NULL is returned if X11 connection failed.
+ static rtc::scoped_refptr<SharedXDisplay> CreateDefault();
+
+ Display* display() { return display_; }
+
+ // Adds a new event `handler` for XEvent's of `type`.
+ void AddEventHandler(int type, XEventHandler* handler);
+
+ // Removes event `handler` added using `AddEventHandler`. Doesn't do anything
+ // if `handler` is not registered.
+ void RemoveEventHandler(int type, XEventHandler* handler);
+
+ // Processes pending XEvents, calling corresponding event handlers.
+ void ProcessPendingXEvents();
+
+ void IgnoreXServerGrabs();
+
+ ~SharedXDisplay();
+
+ SharedXDisplay(const SharedXDisplay&) = delete;
+ SharedXDisplay& operator=(const SharedXDisplay&) = delete;
+
+ protected:
+ // Takes ownership of `display`.
+ explicit SharedXDisplay(Display* display);
+
+ private:
+ typedef std::map<int, std::vector<XEventHandler*> > EventHandlersMap;
+
+ Display* display_;
+
+ EventHandlersMap event_handlers_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_SHARED_X_DISPLAY_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc
new file mode 100644
index 0000000000..3015a474ff
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.cc
@@ -0,0 +1,255 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/window_capturer_x11.h"
+
+#include <X11/Xutil.h>
+#include <X11/extensions/Xcomposite.h>
+#include <X11/extensions/composite.h>
+#include <string.h>
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/linux/x11/shared_x_display.h"
+#include "modules/desktop_capture/linux/x11/window_finder_x11.h"
+#include "modules/desktop_capture/linux/x11/window_list_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/trace_event.h"
+
+namespace webrtc {
+
+WindowCapturerX11::WindowCapturerX11(const DesktopCaptureOptions& options)
+ : x_display_(options.x_display()),
+ atom_cache_(display()),
+ window_finder_(&atom_cache_) {
+ int event_base, error_base, major_version, minor_version;
+ if (XCompositeQueryExtension(display(), &event_base, &error_base) &&
+ XCompositeQueryVersion(display(), &major_version, &minor_version) &&
+ // XCompositeNameWindowPixmap() requires version 0.2
+ (major_version > 0 || minor_version >= 2)) {
+ has_composite_extension_ = true;
+ } else {
+ RTC_LOG(LS_INFO) << "Xcomposite extension not available or too old.";
+ }
+
+ x_display_->AddEventHandler(ConfigureNotify, this);
+}
+
+WindowCapturerX11::~WindowCapturerX11() {
+ x_display_->RemoveEventHandler(ConfigureNotify, this);
+}
+
+bool WindowCapturerX11::GetSourceList(SourceList* sources) {
+ return GetWindowList(&atom_cache_, [this, sources](::Window window) {
+ Source w;
+ w.id = window;
+ w.pid = (pid_t)GetWindowProcessID(window);
+ if (this->GetWindowTitle(window, &w.title)) {
+ sources->push_back(w);
+ }
+ return true;
+ });
+}
+
+bool WindowCapturerX11::SelectSource(SourceId id) {
+ if (!x_server_pixel_buffer_.Init(&atom_cache_, id))
+ return false;
+
+ // Tell the X server to send us window resizing events.
+ XSelectInput(display(), id, StructureNotifyMask);
+
+ selected_window_ = id;
+
+ // In addition to needing X11 server-side support for Xcomposite, it actually
+ // needs to be turned on for the window. If the user has modern
+ // hardware/drivers but isn't using a compositing window manager, that won't
+ // be the case. Here we automatically turn it on.
+
+ // Redirect drawing to an offscreen buffer (ie, turn on compositing). X11
+ // remembers who has requested this and will turn it off for us when we exit.
+ XCompositeRedirectWindow(display(), id, CompositeRedirectAutomatic);
+
+ return true;
+}
+
+bool WindowCapturerX11::FocusOnSelectedSource() {
+ if (!selected_window_)
+ return false;
+
+ unsigned int num_children;
+ ::Window* children;
+ ::Window parent;
+ ::Window root;
+ // Find the root window to pass event to.
+ int status = XQueryTree(display(), selected_window_, &root, &parent,
+ &children, &num_children);
+ if (status == 0) {
+ RTC_LOG(LS_ERROR) << "Failed to query for the root window.";
+ return false;
+ }
+
+ if (children)
+ XFree(children);
+
+ XRaiseWindow(display(), selected_window_);
+
+ // Some window managers (e.g., metacity in GNOME) consider it illegal to
+ // raise a window without also giving it input focus with
+ // _NET_ACTIVE_WINDOW, so XRaiseWindow() on its own isn't enough.
+ Atom atom = XInternAtom(display(), "_NET_ACTIVE_WINDOW", True);
+ if (atom != None) {
+ XEvent xev;
+ xev.xclient.type = ClientMessage;
+ xev.xclient.serial = 0;
+ xev.xclient.send_event = True;
+ xev.xclient.window = selected_window_;
+ xev.xclient.message_type = atom;
+
+ // The format member is set to 8, 16, or 32 and specifies whether the
+ // data should be viewed as a list of bytes, shorts, or longs.
+ xev.xclient.format = 32;
+
+ memset(xev.xclient.data.l, 0, sizeof(xev.xclient.data.l));
+
+ XSendEvent(display(), root, False,
+ SubstructureRedirectMask | SubstructureNotifyMask, &xev);
+ }
+ XFlush(display());
+ return true;
+}
+
+void WindowCapturerX11::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+
+ callback_ = callback;
+}
+
+void WindowCapturerX11::CaptureFrame() {
+ TRACE_EVENT0("webrtc", "WindowCapturerX11::CaptureFrame");
+ x_display_->ProcessPendingXEvents();
+
+ if (!x_server_pixel_buffer_.IsWindowValid()) {
+ RTC_LOG(LS_ERROR) << "The window is no longer valid.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+
+ if (!has_composite_extension_) {
+ // Without the Xcomposite extension we capture when the whole window is
+ // visible on screen and not covered by any other window. This is not
+ // something we want so instead, just bail out.
+ RTC_LOG(LS_ERROR) << "No Xcomposite extension detected.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+
+ if (GetWindowState(&atom_cache_, selected_window_) == IconicState) {
+ // Window is in minimized. Return a 1x1 frame as same as OSX/Win does.
+ std::unique_ptr<DesktopFrame> frame(
+ new BasicDesktopFrame(DesktopSize(1, 1)));
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+ return;
+ }
+
+ std::unique_ptr<DesktopFrame> frame(
+ new BasicDesktopFrame(x_server_pixel_buffer_.window_size()));
+
+ x_server_pixel_buffer_.Synchronize();
+ if (!x_server_pixel_buffer_.CaptureRect(DesktopRect::MakeSize(frame->size()),
+ frame.get())) {
+ RTC_LOG(LS_WARNING) << "Temporarily failed to capture winodw.";
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+
+ frame->mutable_updated_region()->SetRect(
+ DesktopRect::MakeSize(frame->size()));
+ frame->set_top_left(x_server_pixel_buffer_.window_rect().top_left());
+
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+}
+
+bool WindowCapturerX11::IsOccluded(const DesktopVector& pos) {
+ return window_finder_.GetWindowUnderPoint(pos) !=
+ static_cast<WindowId>(selected_window_);
+}
+
+bool WindowCapturerX11::HandleXEvent(const XEvent& event) {
+ if (event.type == ConfigureNotify) {
+ XConfigureEvent xce = event.xconfigure;
+ if (xce.window == selected_window_) {
+ if (!DesktopRectFromXAttributes(xce).equals(
+ x_server_pixel_buffer_.window_rect())) {
+ if (!x_server_pixel_buffer_.Init(&atom_cache_, selected_window_)) {
+ RTC_LOG(LS_ERROR)
+ << "Failed to initialize pixel buffer after resizing.";
+ }
+ }
+ }
+ }
+
+ // Always returns false, so other observers can still receive the events.
+ return false;
+}
+
+bool WindowCapturerX11::GetWindowTitle(::Window window, std::string* title) {
+ int status;
+ bool result = false;
+ XTextProperty window_name;
+ window_name.value = nullptr;
+ if (window) {
+ status = XGetWMName(display(), window, &window_name);
+ if (status && window_name.value && window_name.nitems) {
+ int cnt;
+ char** list = nullptr;
+ status =
+ Xutf8TextPropertyToTextList(display(), &window_name, &list, &cnt);
+ if (status >= Success && cnt && *list) {
+ if (cnt > 1) {
+ RTC_LOG(LS_INFO) << "Window has " << cnt
+ << " text properties, only using the first one.";
+ }
+ *title = *list;
+ result = true;
+ }
+ if (list)
+ XFreeStringList(list);
+ }
+ if (window_name.value)
+ XFree(window_name.value);
+ }
+ return result;
+}
+
+int WindowCapturerX11::GetWindowProcessID(::Window window) {
+ // Get _NET_WM_PID property of the window.
+ Atom process_atom = XInternAtom(display(), "_NET_WM_PID", True);
+ XWindowProperty<uint32_t> process_id(display(), window, process_atom);
+
+ return process_id.is_valid() ? *process_id.data() : 0;
+}
+
+// static
+std::unique_ptr<DesktopCapturer> WindowCapturerX11::CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options) {
+ if (!options.x_display())
+ return nullptr;
+ return std::unique_ptr<DesktopCapturer>(new WindowCapturerX11(options));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.h
new file mode 100644
index 0000000000..cfd29eca66
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_capturer_x11.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_CAPTURER_X11_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_CAPTURER_X11_H_
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+#include <memory>
+#include <string>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/linux/x11/shared_x_display.h"
+#include "modules/desktop_capture/linux/x11/x_window_property.h"
+#include "modules/desktop_capture/linux/x11/window_finder_x11.h"
+#include "modules/desktop_capture/linux/x11/x_atom_cache.h"
+#include "modules/desktop_capture/linux/x11/x_server_pixel_buffer.h"
+
+namespace webrtc {
+
+class WindowCapturerX11 : public DesktopCapturer,
+ public SharedXDisplay::XEventHandler {
+ public:
+ explicit WindowCapturerX11(const DesktopCaptureOptions& options);
+ ~WindowCapturerX11() override;
+
+ WindowCapturerX11(const WindowCapturerX11&) = delete;
+ WindowCapturerX11& operator=(const WindowCapturerX11&) = delete;
+
+ static std::unique_ptr<DesktopCapturer> CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options);
+
+ // DesktopCapturer interface.
+ void Start(Callback* callback) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+ bool FocusOnSelectedSource() override;
+ bool IsOccluded(const DesktopVector& pos) override;
+
+ // SharedXDisplay::XEventHandler interface.
+ bool HandleXEvent(const XEvent& event) override;
+
+ private:
+ Display* display() { return x_display_->display(); }
+
+ // Returns window title for the specified X `window`.
+ bool GetWindowTitle(::Window window, std::string* title);
+
+ // Returns the id of the owning process.
+ int GetWindowProcessID(::Window window);
+
+ Callback* callback_ = nullptr;
+
+ rtc::scoped_refptr<SharedXDisplay> x_display_;
+
+ bool has_composite_extension_ = false;
+
+ ::Window selected_window_ = 0;
+ XServerPixelBuffer x_server_pixel_buffer_;
+ XAtomCache atom_cache_;
+ WindowFinderX11 window_finder_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_CAPTURER_X11_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.cc
new file mode 100644
index 0000000000..dec17ab51f
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.cc
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/window_finder_x11.h"
+
+#include <X11/X.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/linux/x11/window_list_utils.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+WindowFinderX11::WindowFinderX11(XAtomCache* cache) : cache_(cache) {
+ RTC_DCHECK(cache_);
+}
+
+WindowFinderX11::~WindowFinderX11() = default;
+
+WindowId WindowFinderX11::GetWindowUnderPoint(DesktopVector point) {
+ WindowId id = kNullWindowId;
+ GetWindowList(cache_, [&id, this, point](::Window window) {
+ DesktopRect rect;
+ if (GetWindowRect(this->cache_->display(), window, &rect) &&
+ rect.Contains(point)) {
+ id = window;
+ return false;
+ }
+ return true;
+ });
+ return id;
+}
+
+// static
+std::unique_ptr<WindowFinder> WindowFinder::Create(
+ const WindowFinder::Options& options) {
+ if (options.cache == nullptr) {
+ return nullptr;
+ }
+
+ return std::make_unique<WindowFinderX11>(options.cache);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.h
new file mode 100644
index 0000000000..91de876417
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_finder_x11.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_FINDER_X11_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_FINDER_X11_H_
+
+#include "modules/desktop_capture/window_finder.h"
+
+namespace webrtc {
+
+class XAtomCache;
+
+// The implementation of WindowFinder for X11.
+class WindowFinderX11 final : public WindowFinder {
+ public:
+ explicit WindowFinderX11(XAtomCache* cache);
+ ~WindowFinderX11() override;
+
+ // WindowFinder implementation.
+ WindowId GetWindowUnderPoint(DesktopVector point) override;
+
+ private:
+ XAtomCache* const cache_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_FINDER_X11_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.cc
new file mode 100644
index 0000000000..ff2d467e29
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.cc
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/window_list_utils.h"
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <string.h>
+
+#include <algorithm>
+
+#include "modules/desktop_capture/linux/x11/x_error_trap.h"
+#include "modules/desktop_capture/linux/x11/x_window_property.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+namespace {
+
+class DeferXFree {
+ public:
+ explicit DeferXFree(void* data) : data_(data) {}
+ ~DeferXFree();
+
+ private:
+ void* const data_;
+};
+
+DeferXFree::~DeferXFree() {
+ if (data_)
+ XFree(data_);
+}
+
+// Iterates through `window` hierarchy to find first visible window, i.e. one
+// that has WM_STATE property set to NormalState.
+// See http://tronche.com/gui/x/icccm/sec-4.html#s-4.1.3.1 .
+::Window GetApplicationWindow(XAtomCache* cache, ::Window window) {
+ int32_t state = GetWindowState(cache, window);
+ if (state == NormalState) {
+ // Window has WM_STATE==NormalState. Return it.
+ return window;
+ } else if (state == IconicState) {
+ // Window is in minimized. Skip it.
+ return 0;
+ }
+
+ RTC_DCHECK_EQ(state, WithdrawnState);
+ // If the window is in WithdrawnState then look at all of its children.
+ ::Window root, parent;
+ ::Window* children;
+ unsigned int num_children;
+ if (!XQueryTree(cache->display(), window, &root, &parent, &children,
+ &num_children)) {
+ RTC_LOG(LS_ERROR) << "Failed to query for child windows although window"
+ "does not have a valid WM_STATE.";
+ return 0;
+ }
+ ::Window app_window = 0;
+ for (unsigned int i = 0; i < num_children; ++i) {
+ app_window = GetApplicationWindow(cache, children[i]);
+ if (app_window)
+ break;
+ }
+
+ if (children)
+ XFree(children);
+ return app_window;
+}
+
+// Returns true if the `window` is a desktop element.
+bool IsDesktopElement(XAtomCache* cache, ::Window window) {
+ RTC_DCHECK(cache);
+ if (window == 0)
+ return false;
+
+ // First look for _NET_WM_WINDOW_TYPE. The standard
+ // (http://standards.freedesktop.org/wm-spec/latest/ar01s05.html#id2760306)
+ // says this hint *should* be present on all windows, and we use the existence
+ // of _NET_WM_WINDOW_TYPE_NORMAL in the property to indicate a window is not
+ // a desktop element (that is, only "normal" windows should be shareable).
+ XWindowProperty<uint32_t> window_type(cache->display(), window,
+ cache->WindowType());
+ if (window_type.is_valid() && window_type.size() > 0) {
+ uint32_t* end = window_type.data() + window_type.size();
+ bool is_normal =
+ (end != std::find(window_type.data(), end, cache->WindowTypeNormal()));
+ return !is_normal;
+ }
+
+ // Fall back on using the hint.
+ XClassHint class_hint;
+ Status status = XGetClassHint(cache->display(), window, &class_hint);
+ if (status == 0) {
+ // No hints, assume this is a normal application window.
+ return false;
+ }
+
+ DeferXFree free_res_name(class_hint.res_name);
+ DeferXFree free_res_class(class_hint.res_class);
+ return strcmp("gnome-panel", class_hint.res_name) == 0 ||
+ strcmp("desktop_window", class_hint.res_name) == 0;
+}
+
+} // namespace
+
+int32_t GetWindowState(XAtomCache* cache, ::Window window) {
+ // Get WM_STATE property of the window.
+ XWindowProperty<uint32_t> window_state(cache->display(), window,
+ cache->WmState());
+
+ // WM_STATE is considered to be set to WithdrawnState when it missing.
+ return window_state.is_valid() ? *window_state.data() : WithdrawnState;
+}
+
+bool GetWindowList(XAtomCache* cache,
+ rtc::FunctionView<bool(::Window)> on_window) {
+ RTC_DCHECK(cache);
+ RTC_DCHECK(on_window);
+ ::Display* const display = cache->display();
+
+ int failed_screens = 0;
+ const int num_screens = XScreenCount(display);
+ for (int screen = 0; screen < num_screens; screen++) {
+ ::Window root_window = XRootWindow(display, screen);
+ ::Window parent;
+ ::Window* children;
+ unsigned int num_children;
+ {
+ XErrorTrap error_trap(display);
+ if (XQueryTree(display, root_window, &root_window, &parent, &children,
+ &num_children) == 0 ||
+ error_trap.GetLastErrorAndDisable() != 0) {
+ failed_screens++;
+ RTC_LOG(LS_ERROR) << "Failed to query for child windows for screen "
+ << screen;
+ continue;
+ }
+ }
+
+ DeferXFree free_children(children);
+
+ for (unsigned int i = 0; i < num_children; i++) {
+ // Iterates in reverse order to return windows from front to back.
+ ::Window app_window =
+ GetApplicationWindow(cache, children[num_children - 1 - i]);
+ if (app_window && !IsDesktopElement(cache, app_window)) {
+ if (!on_window(app_window)) {
+ return true;
+ }
+ }
+ }
+ }
+
+ return failed_screens < num_screens;
+}
+
+bool GetWindowRect(::Display* display,
+ ::Window window,
+ DesktopRect* rect,
+ XWindowAttributes* attributes /* = nullptr */) {
+ XWindowAttributes local_attributes;
+ int offset_x;
+ int offset_y;
+ if (attributes == nullptr) {
+ attributes = &local_attributes;
+ }
+
+ {
+ XErrorTrap error_trap(display);
+ if (!XGetWindowAttributes(display, window, attributes) ||
+ error_trap.GetLastErrorAndDisable() != 0) {
+ return false;
+ }
+ }
+ *rect = DesktopRectFromXAttributes(*attributes);
+
+ {
+ XErrorTrap error_trap(display);
+ ::Window child;
+ if (!XTranslateCoordinates(display, window, attributes->root, -rect->left(),
+ -rect->top(), &offset_x, &offset_y, &child) ||
+ error_trap.GetLastErrorAndDisable() != 0) {
+ return false;
+ }
+ }
+ rect->Translate(offset_x, offset_y);
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.h
new file mode 100644
index 0000000000..923842df14
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/window_list_utils.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_LIST_UTILS_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_LIST_UTILS_H_
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <stdint.h>
+
+#include "api/function_view.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/linux/x11/x_atom_cache.h"
+
+namespace webrtc {
+
+// Synchronously iterates all on-screen windows in `cache`.display() in
+// decreasing z-order and sends them one-by-one to `on_window` function before
+// GetWindowList() returns. If `on_window` returns false, this function ignores
+// other windows and returns immediately. GetWindowList() returns false if
+// native APIs failed. If multiple screens are attached to the `display`, this
+// function returns false only when native APIs failed on all screens. Menus,
+// panels and minimized windows will be ignored.
+bool GetWindowList(XAtomCache* cache,
+ rtc::FunctionView<bool(::Window)> on_window);
+
+// Returns WM_STATE property of the `window`. This function returns
+// WithdrawnState if the `window` is missing.
+int32_t GetWindowState(XAtomCache* cache, ::Window window);
+
+// Returns the rectangle of the `window` in the coordinates of `display`. This
+// function returns false if native APIs failed. If `attributes` is provided, it
+// will be filled with the attributes of `window`. The `rect` is in system
+// coordinate, i.e. the primary monitor always starts from (0, 0).
+bool GetWindowRect(::Display* display,
+ ::Window window,
+ DesktopRect* rect,
+ XWindowAttributes* attributes = nullptr);
+
+// Creates a DesktopRect from `attributes`.
+template <typename T>
+DesktopRect DesktopRectFromXAttributes(const T& attributes) {
+ return DesktopRect::MakeXYWH(attributes.x, attributes.y, attributes.width,
+ attributes.height);
+}
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_WINDOW_LIST_UTILS_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.cc
new file mode 100644
index 0000000000..157ba8b8fd
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.cc
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/x_atom_cache.h"
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+XAtomCache::XAtomCache(::Display* display) : display_(display) {
+ RTC_DCHECK(display_);
+}
+
+XAtomCache::~XAtomCache() = default;
+
+::Display* XAtomCache::display() const {
+ return display_;
+}
+
+Atom XAtomCache::WmState() {
+ return CreateIfNotExist(&wm_state_, "WM_STATE");
+}
+
+Atom XAtomCache::WindowType() {
+ return CreateIfNotExist(&window_type_, "_NET_WM_WINDOW_TYPE");
+}
+
+Atom XAtomCache::WindowTypeNormal() {
+ return CreateIfNotExist(&window_type_normal_, "_NET_WM_WINDOW_TYPE_NORMAL");
+}
+
+Atom XAtomCache::IccProfile() {
+ return CreateIfNotExist(&icc_profile_, "_ICC_PROFILE");
+}
+
+Atom XAtomCache::CreateIfNotExist(Atom* atom, const char* name) {
+ RTC_DCHECK(atom);
+ if (*atom == None) {
+ *atom = XInternAtom(display(), name, True);
+ }
+ return *atom;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.h
new file mode 100644
index 0000000000..39d957e98b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_atom_cache.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ATOM_CACHE_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ATOM_CACHE_H_
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+namespace webrtc {
+
+// A cache of Atom. Each Atom object is created on demand.
+class XAtomCache final {
+ public:
+ explicit XAtomCache(::Display* display);
+ ~XAtomCache();
+
+ ::Display* display() const;
+
+ Atom WmState();
+ Atom WindowType();
+ Atom WindowTypeNormal();
+ Atom IccProfile();
+
+ private:
+ // If |*atom| is None, this function uses XInternAtom() to retrieve an Atom.
+ Atom CreateIfNotExist(Atom* atom, const char* name);
+
+ ::Display* const display_;
+ Atom wm_state_ = None;
+ Atom window_type_ = None;
+ Atom window_type_normal_ = None;
+ Atom icc_profile_ = None;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ATOM_CACHE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.cc
new file mode 100644
index 0000000000..3314dd286c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/x_error_trap.h"
+
+#include <stddef.h>
+
+#include <limits>
+
+#include "rtc_base/checks.h"
+
+
+namespace webrtc {
+
+Bool XErrorTrap::XServerErrorHandler(Display* display, xReply* rep,
+ char* /* buf */, int /* len */,
+ XPointer data) {
+ XErrorTrap* self = reinterpret_cast<XErrorTrap*>(data);
+ if (rep->generic.type != X_Error ||
+ // Overflow-safe last_request_read <= last_ignored_request_ for skipping
+ // async replies from requests before XErrorTrap was created.
+ self->last_ignored_request_ - display->last_request_read <
+ std::numeric_limits<unsigned long>::max() >> 1)
+ return False;
+ self->last_xserver_error_code_ = rep->error.errorCode;
+ return True;
+}
+
+XErrorTrap::XErrorTrap(Display* display)
+ : display_(display),
+ last_xserver_error_code_(0),
+ enabled_(true) {
+ // Use async_handlers instead of XSetErrorHandler(). async_handlers can
+ // remain in place and then be safely removed at the right time even if a
+ // handler change happens concurrently on another thread. async_handlers
+ // are processed first and so can prevent errors reaching the global
+ // XSetErrorHandler handler. They also will not see errors from or affect
+ // handling of errors on other Displays, which may be processed on other
+ // threads.
+ LockDisplay(display);
+ async_handler_.next = display->async_handlers;
+ async_handler_.handler = XServerErrorHandler;
+ async_handler_.data = reinterpret_cast<XPointer>(this);
+ display->async_handlers = &async_handler_;
+ last_ignored_request_ = display->request;
+ UnlockDisplay(display);
+}
+
+int XErrorTrap::GetLastErrorAndDisable() {
+ assert(enabled_);
+ enabled_ = false;
+ LockDisplay(display_);
+ DeqAsyncHandler(display_, &async_handler_);
+ UnlockDisplay(display_);
+ return last_xserver_error_code_;
+}
+
+XErrorTrap::~XErrorTrap() {
+ if (enabled_)
+ GetLastErrorAndDisable();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.h
new file mode 100644
index 0000000000..df7e86bf03
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_error_trap.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ERROR_TRAP_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ERROR_TRAP_H_
+
+#include <X11/Xlibint.h>
+#undef max // Xlibint.h defines this and it breaks std::max
+#undef min // Xlibint.h defines this and it breaks std::min
+
+namespace webrtc {
+
+// Helper class that registers X Window error handler. Caller can use
+// GetLastErrorAndDisable() to get the last error that was caught, if any.
+// An XErrorTrap may be constructed on any thread, but errors are collected
+// from all threads and so |display| should be used only on one thread.
+// Other Displays are unaffected.
+class XErrorTrap {
+ public:
+ explicit XErrorTrap(Display* display);
+ ~XErrorTrap();
+
+ XErrorTrap(const XErrorTrap&) = delete;
+ XErrorTrap& operator=(const XErrorTrap&) = delete;
+
+ // Returns last error and removes unregisters the error handler.
+ // Must not be called more than once.
+ int GetLastErrorAndDisable();
+
+ private:
+ static Bool XServerErrorHandler(Display* display, xReply* rep,
+ char* /* buf */, int /* len */,
+ XPointer data);
+
+ _XAsyncHandler async_handler_;
+ Display* display_;
+ unsigned long last_ignored_request_;
+ int last_xserver_error_code_;
+ bool enabled_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_X_ERROR_TRAP_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc
new file mode 100644
index 0000000000..fd6fc7daf4
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.cc
@@ -0,0 +1,379 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/x_server_pixel_buffer.h"
+
+#include <X11/Xutil.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/ipc.h>
+#include <sys/shm.h>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/linux/x11/window_list_utils.h"
+#include "modules/desktop_capture/linux/x11/x_error_trap.h"
+#include "modules/desktop_capture/linux/x11/x_window_property.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+namespace {
+
+// Returns the number of bits `mask` has to be shifted left so its last
+// (most-significant) bit set becomes the most-significant bit of the word.
+// When `mask` is 0 the function returns 31.
+uint32_t MaskToShift(uint32_t mask) {
+ int shift = 0;
+ if ((mask & 0xffff0000u) == 0) {
+ mask <<= 16;
+ shift += 16;
+ }
+ if ((mask & 0xff000000u) == 0) {
+ mask <<= 8;
+ shift += 8;
+ }
+ if ((mask & 0xf0000000u) == 0) {
+ mask <<= 4;
+ shift += 4;
+ }
+ if ((mask & 0xc0000000u) == 0) {
+ mask <<= 2;
+ shift += 2;
+ }
+ if ((mask & 0x80000000u) == 0)
+ shift += 1;
+
+ return shift;
+}
+
+// Returns true if `image` is in RGB format.
+bool IsXImageRGBFormat(XImage* image) {
+ return image->bits_per_pixel == 32 && image->red_mask == 0xff0000 &&
+ image->green_mask == 0xff00 && image->blue_mask == 0xff;
+}
+
+// We expose two forms of blitting to handle variations in the pixel format.
+// In FastBlit(), the operation is effectively a memcpy.
+void FastBlit(XImage* x_image,
+ uint8_t* src_pos,
+ const DesktopRect& rect,
+ DesktopFrame* frame) {
+ RTC_DCHECK_LE(frame->top_left().x(), rect.left());
+ RTC_DCHECK_LE(frame->top_left().y(), rect.top());
+
+ int src_stride = x_image->bytes_per_line;
+ int dst_x = rect.left() - frame->top_left().x();
+ int dst_y = rect.top() - frame->top_left().y();
+
+ uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
+ dst_pos += dst_x * DesktopFrame::kBytesPerPixel;
+
+ int height = rect.height();
+ int row_bytes = rect.width() * DesktopFrame::kBytesPerPixel;
+ for (int y = 0; y < height; ++y) {
+ memcpy(dst_pos, src_pos, row_bytes);
+ src_pos += src_stride;
+ dst_pos += frame->stride();
+ }
+}
+
+void SlowBlit(XImage* x_image,
+ uint8_t* src_pos,
+ const DesktopRect& rect,
+ DesktopFrame* frame) {
+ RTC_DCHECK_LE(frame->top_left().x(), rect.left());
+ RTC_DCHECK_LE(frame->top_left().y(), rect.top());
+
+ int src_stride = x_image->bytes_per_line;
+ int dst_x = rect.left() - frame->top_left().x();
+ int dst_y = rect.top() - frame->top_left().y();
+ int width = rect.width(), height = rect.height();
+
+ uint32_t red_mask = x_image->red_mask;
+ uint32_t green_mask = x_image->red_mask;
+ uint32_t blue_mask = x_image->blue_mask;
+
+ uint32_t red_shift = MaskToShift(red_mask);
+ uint32_t green_shift = MaskToShift(green_mask);
+ uint32_t blue_shift = MaskToShift(blue_mask);
+
+ int bits_per_pixel = x_image->bits_per_pixel;
+
+ uint8_t* dst_pos = frame->data() + frame->stride() * dst_y;
+ dst_pos += dst_x * DesktopFrame::kBytesPerPixel;
+ // TODO(hclam): Optimize, perhaps using MMX code or by converting to
+ // YUV directly.
+ // TODO(sergeyu): This code doesn't handle XImage byte order properly and
+ // won't work with 24bpp images. Fix it.
+ for (int y = 0; y < height; y++) {
+ uint32_t* dst_pos_32 = reinterpret_cast<uint32_t*>(dst_pos);
+ uint32_t* src_pos_32 = reinterpret_cast<uint32_t*>(src_pos);
+ uint16_t* src_pos_16 = reinterpret_cast<uint16_t*>(src_pos);
+ for (int x = 0; x < width; x++) {
+ // Dereference through an appropriately-aligned pointer.
+ uint32_t pixel;
+ if (bits_per_pixel == 32) {
+ pixel = src_pos_32[x];
+ } else if (bits_per_pixel == 16) {
+ pixel = src_pos_16[x];
+ } else {
+ pixel = src_pos[x];
+ }
+ uint32_t r = (pixel & red_mask) << red_shift;
+ uint32_t g = (pixel & green_mask) << green_shift;
+ uint32_t b = (pixel & blue_mask) << blue_shift;
+ // Write as 32-bit RGB.
+ dst_pos_32[x] =
+ ((r >> 8) & 0xff0000) | ((g >> 16) & 0xff00) | ((b >> 24) & 0xff);
+ }
+ dst_pos += frame->stride();
+ src_pos += src_stride;
+ }
+}
+
+} // namespace
+
+XServerPixelBuffer::XServerPixelBuffer() {}
+
+XServerPixelBuffer::~XServerPixelBuffer() {
+ Release();
+}
+
+void XServerPixelBuffer::Release() {
+ if (x_image_) {
+ XDestroyImage(x_image_);
+ x_image_ = nullptr;
+ }
+ if (x_shm_image_) {
+ XDestroyImage(x_shm_image_);
+ x_shm_image_ = nullptr;
+ }
+ if (shm_pixmap_) {
+ XFreePixmap(display_, shm_pixmap_);
+ shm_pixmap_ = 0;
+ }
+ if (shm_gc_) {
+ XFreeGC(display_, shm_gc_);
+ shm_gc_ = nullptr;
+ }
+
+ ReleaseSharedMemorySegment();
+
+ window_ = 0;
+}
+
+void XServerPixelBuffer::ReleaseSharedMemorySegment() {
+ if (!shm_segment_info_)
+ return;
+ if (shm_segment_info_->shmaddr != nullptr)
+ shmdt(shm_segment_info_->shmaddr);
+ if (shm_segment_info_->shmid != -1)
+ shmctl(shm_segment_info_->shmid, IPC_RMID, 0);
+ delete shm_segment_info_;
+ shm_segment_info_ = nullptr;
+}
+
+bool XServerPixelBuffer::Init(XAtomCache* cache, Window window) {
+ Release();
+ display_ = cache->display();
+
+ XWindowAttributes attributes;
+ if (!GetWindowRect(display_, window, &window_rect_, &attributes)) {
+ return false;
+ }
+
+ if (cache->IccProfile() != None) {
+ // `window` is the root window when doing screen capture.
+ XWindowProperty<uint8_t> icc_profile_property(cache->display(), window,
+ cache->IccProfile());
+ if (icc_profile_property.is_valid() && icc_profile_property.size() > 0) {
+ icc_profile_ = std::vector<uint8_t>(
+ icc_profile_property.data(),
+ icc_profile_property.data() + icc_profile_property.size());
+ } else {
+ RTC_LOG(LS_WARNING) << "Failed to get icc profile";
+ }
+ }
+
+ window_ = window;
+ InitShm(attributes);
+
+ return true;
+}
+
+void XServerPixelBuffer::InitShm(const XWindowAttributes& attributes) {
+ Visual* default_visual = attributes.visual;
+ int default_depth = attributes.depth;
+
+ int major, minor;
+ Bool have_pixmaps;
+ if (!XShmQueryVersion(display_, &major, &minor, &have_pixmaps)) {
+ // Shared memory not supported. CaptureRect will use the XImage API instead.
+ return;
+ }
+
+ bool using_shm = false;
+ shm_segment_info_ = new XShmSegmentInfo;
+ shm_segment_info_->shmid = -1;
+ shm_segment_info_->shmaddr = nullptr;
+ shm_segment_info_->readOnly = False;
+ x_shm_image_ = XShmCreateImage(display_, default_visual, default_depth,
+ ZPixmap, 0, shm_segment_info_,
+ window_rect_.width(), window_rect_.height());
+ if (x_shm_image_) {
+ shm_segment_info_->shmid =
+ shmget(IPC_PRIVATE, x_shm_image_->bytes_per_line * x_shm_image_->height,
+ IPC_CREAT | 0600);
+ if (shm_segment_info_->shmid != -1) {
+ void* shmat_result = shmat(shm_segment_info_->shmid, 0, 0);
+ if (shmat_result != reinterpret_cast<void*>(-1)) {
+ shm_segment_info_->shmaddr = reinterpret_cast<char*>(shmat_result);
+ x_shm_image_->data = shm_segment_info_->shmaddr;
+
+ XErrorTrap error_trap(display_);
+ using_shm = XShmAttach(display_, shm_segment_info_);
+ XSync(display_, False);
+ if (error_trap.GetLastErrorAndDisable() != 0)
+ using_shm = false;
+ if (using_shm) {
+ RTC_LOG(LS_VERBOSE)
+ << "Using X shared memory segment " << shm_segment_info_->shmid;
+ }
+ }
+ } else {
+ RTC_LOG(LS_WARNING) << "Failed to get shared memory segment. "
+ "Performance may be degraded.";
+ }
+ }
+
+ if (!using_shm) {
+ RTC_LOG(LS_WARNING)
+ << "Not using shared memory. Performance may be degraded.";
+ ReleaseSharedMemorySegment();
+ return;
+ }
+
+ if (have_pixmaps)
+ have_pixmaps = InitPixmaps(default_depth);
+
+ shmctl(shm_segment_info_->shmid, IPC_RMID, 0);
+ shm_segment_info_->shmid = -1;
+
+ RTC_LOG(LS_VERBOSE) << "Using X shared memory extension v" << major << "."
+ << minor << " with" << (have_pixmaps ? "" : "out")
+ << " pixmaps.";
+}
+
+bool XServerPixelBuffer::InitPixmaps(int depth) {
+ if (XShmPixmapFormat(display_) != ZPixmap)
+ return false;
+
+ {
+ XErrorTrap error_trap(display_);
+ shm_pixmap_ = XShmCreatePixmap(
+ display_, window_, shm_segment_info_->shmaddr, shm_segment_info_,
+ window_rect_.width(), window_rect_.height(), depth);
+ XSync(display_, False);
+ if (error_trap.GetLastErrorAndDisable() != 0) {
+ // `shm_pixmap_` is not not valid because the request was not processed
+ // by the X Server, so zero it.
+ shm_pixmap_ = 0;
+ return false;
+ }
+ }
+
+ {
+ XErrorTrap error_trap(display_);
+ XGCValues shm_gc_values;
+ shm_gc_values.subwindow_mode = IncludeInferiors;
+ shm_gc_values.graphics_exposures = False;
+ shm_gc_ = XCreateGC(display_, window_,
+ GCSubwindowMode | GCGraphicsExposures, &shm_gc_values);
+ XSync(display_, False);
+ if (error_trap.GetLastErrorAndDisable() != 0) {
+ XFreePixmap(display_, shm_pixmap_);
+ shm_pixmap_ = 0;
+ shm_gc_ = 0; // See shm_pixmap_ comment above.
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool XServerPixelBuffer::IsWindowValid() const {
+ XWindowAttributes attributes;
+ {
+ XErrorTrap error_trap(display_);
+ if (!XGetWindowAttributes(display_, window_, &attributes) ||
+ error_trap.GetLastErrorAndDisable() != 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void XServerPixelBuffer::Synchronize() {
+ if (shm_segment_info_ && !shm_pixmap_) {
+ // XShmGetImage can fail if the display is being reconfigured.
+ XErrorTrap error_trap(display_);
+ // XShmGetImage fails if the window is partially out of screen.
+ xshm_get_image_succeeded_ =
+ XShmGetImage(display_, window_, x_shm_image_, 0, 0, AllPlanes);
+ }
+}
+
+bool XServerPixelBuffer::CaptureRect(const DesktopRect& rect,
+ DesktopFrame* frame) {
+ RTC_DCHECK_LE(rect.right(), window_rect_.width());
+ RTC_DCHECK_LE(rect.bottom(), window_rect_.height());
+
+ XImage* image;
+ uint8_t* data;
+
+ if (shm_segment_info_ && (shm_pixmap_ || xshm_get_image_succeeded_)) {
+ if (shm_pixmap_) {
+ XCopyArea(display_, window_, shm_pixmap_, shm_gc_, rect.left(),
+ rect.top(), rect.width(), rect.height(), rect.left(),
+ rect.top());
+ XSync(display_, False);
+ }
+
+ image = x_shm_image_;
+ data = reinterpret_cast<uint8_t*>(image->data) +
+ rect.top() * image->bytes_per_line +
+ rect.left() * image->bits_per_pixel / 8;
+
+ } else {
+ if (x_image_)
+ XDestroyImage(x_image_);
+ x_image_ = XGetImage(display_, window_, rect.left(), rect.top(),
+ rect.width(), rect.height(), AllPlanes, ZPixmap);
+ if (!x_image_)
+ return false;
+
+ image = x_image_;
+ data = reinterpret_cast<uint8_t*>(image->data);
+ }
+
+ if (IsXImageRGBFormat(image)) {
+ FastBlit(image, data, rect, frame);
+ } else {
+ SlowBlit(image, data, rect, frame);
+ }
+
+ if (!icc_profile_.empty())
+ frame->set_icc_profile(icc_profile_);
+
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.h
new file mode 100644
index 0000000000..38af3a3e76
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_server_pixel_buffer.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// Don't include this file in any .h files because it pulls in some X headers.
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_X_SERVER_PIXEL_BUFFER_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_X_SERVER_PIXEL_BUFFER_H_
+
+#include <X11/Xutil.h>
+#include <X11/extensions/XShm.h>
+
+#include <memory>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+class DesktopFrame;
+class XAtomCache;
+
+// A class to allow the X server's pixel buffer to be accessed as efficiently
+// as possible.
+class XServerPixelBuffer {
+ public:
+ XServerPixelBuffer();
+ ~XServerPixelBuffer();
+
+ XServerPixelBuffer(const XServerPixelBuffer&) = delete;
+ XServerPixelBuffer& operator=(const XServerPixelBuffer&) = delete;
+
+ void Release();
+
+ // Allocate (or reallocate) the pixel buffer for `window`. Returns false in
+ // case of an error (e.g. window doesn't exist).
+ bool Init(XAtomCache* cache, Window window);
+
+ bool is_initialized() { return window_ != 0; }
+
+ // Returns the size of the window the buffer was initialized for.
+ DesktopSize window_size() { return window_rect_.size(); }
+
+ // Returns the rectangle of the window the buffer was initialized for.
+ const DesktopRect& window_rect() { return window_rect_; }
+
+ // Returns true if the window can be found.
+ bool IsWindowValid() const;
+
+ // If shared memory is being used without pixmaps, synchronize this pixel
+ // buffer with the root window contents (otherwise, this is a no-op).
+ // This is to avoid doing a full-screen capture for each individual
+ // rectangle in the capture list, when it only needs to be done once at the
+ // beginning.
+ void Synchronize();
+
+ // Capture the specified rectangle and stores it in the `frame`. In the case
+ // where the full-screen data is captured by Synchronize(), this simply
+ // returns the pointer without doing any more work. The caller must ensure
+ // that `rect` is not larger than window_size().
+ bool CaptureRect(const DesktopRect& rect, DesktopFrame* frame);
+
+ private:
+ void ReleaseSharedMemorySegment();
+
+ void InitShm(const XWindowAttributes& attributes);
+ bool InitPixmaps(int depth);
+
+ Display* display_ = nullptr;
+ Window window_ = 0;
+ DesktopRect window_rect_;
+ XImage* x_image_ = nullptr;
+ XShmSegmentInfo* shm_segment_info_ = nullptr;
+ XImage* x_shm_image_ = nullptr;
+ Pixmap shm_pixmap_ = 0;
+ GC shm_gc_ = nullptr;
+ bool xshm_get_image_succeeded_ = false;
+ std::vector<uint8_t> icc_profile_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_X_SERVER_PIXEL_BUFFER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.cc b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.cc
new file mode 100644
index 0000000000..5e16dac404
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.cc
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/linux/x11/x_window_property.h"
+
+namespace webrtc {
+
+XWindowPropertyBase::XWindowPropertyBase(Display* display,
+ Window window,
+ Atom property,
+ int expected_size) {
+ const int kBitsPerByte = 8;
+ Atom actual_type;
+ int actual_format;
+ unsigned long bytes_after; // NOLINT: type required by XGetWindowProperty
+ int status = XGetWindowProperty(display, window, property, 0L, ~0L, False,
+ AnyPropertyType, &actual_type, &actual_format,
+ &size_, &bytes_after, &data_);
+ if (status != Success) {
+ data_ = nullptr;
+ return;
+ }
+ if ((expected_size * kBitsPerByte) != actual_format) {
+ size_ = 0;
+ return;
+ }
+
+ is_valid_ = true;
+}
+
+XWindowPropertyBase::~XWindowPropertyBase() {
+ if (data_)
+ XFree(data_);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.h b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.h
new file mode 100644
index 0000000000..28dfb97311
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/linux/x11/x_window_property.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_LINUX_X11_X_WINDOW_PROPERTY_H_
+#define MODULES_DESKTOP_CAPTURE_LINUX_X11_X_WINDOW_PROPERTY_H_
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+
+namespace webrtc {
+
+class XWindowPropertyBase {
+ public:
+ XWindowPropertyBase(Display* display,
+ Window window,
+ Atom property,
+ int expected_size);
+ virtual ~XWindowPropertyBase();
+
+ XWindowPropertyBase(const XWindowPropertyBase&) = delete;
+ XWindowPropertyBase& operator=(const XWindowPropertyBase&) = delete;
+
+ // True if we got properly value successfully.
+ bool is_valid() const { return is_valid_; }
+
+ // Size and value of the property.
+ size_t size() const { return size_; }
+
+ protected:
+ unsigned char* data_ = nullptr;
+
+ private:
+ bool is_valid_ = false;
+ unsigned long size_ = 0; // NOLINT: type required by XGetWindowProperty
+};
+
+// Convenience wrapper for XGetWindowProperty() results.
+template <class PropertyType>
+class XWindowProperty : public XWindowPropertyBase {
+ public:
+ XWindowProperty(Display* display, const Window window, const Atom property)
+ : XWindowPropertyBase(display, window, property, sizeof(PropertyType)) {}
+ ~XWindowProperty() override = default;
+
+ XWindowProperty(const XWindowProperty&) = delete;
+ XWindowProperty& operator=(const XWindowProperty&) = delete;
+
+ const PropertyType* data() const {
+ return reinterpret_cast<PropertyType*>(data_);
+ }
+ PropertyType* data() { return reinterpret_cast<PropertyType*>(data_); }
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_LINUX_X11_X_WINDOW_PROPERTY_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.h b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.h
new file mode 100644
index 0000000000..2ad5474e44
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_H_
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include <vector>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// Describes the configuration of a specific display.
+struct MacDisplayConfiguration {
+ MacDisplayConfiguration();
+ MacDisplayConfiguration(const MacDisplayConfiguration& other);
+ MacDisplayConfiguration(MacDisplayConfiguration&& other);
+ ~MacDisplayConfiguration();
+
+ MacDisplayConfiguration& operator=(const MacDisplayConfiguration& other);
+ MacDisplayConfiguration& operator=(MacDisplayConfiguration&& other);
+
+ // Cocoa identifier for this display.
+ CGDirectDisplayID id = 0;
+
+ // Bounds of this display in Density-Independent Pixels (DIPs).
+ DesktopRect bounds;
+
+ // Bounds of this display in physical pixels.
+ DesktopRect pixel_bounds;
+
+ // Scale factor from DIPs to physical pixels.
+ float dip_to_pixel_scale = 1.0f;
+
+ // Display type, built-in or external.
+ bool is_builtin;
+};
+
+typedef std::vector<MacDisplayConfiguration> MacDisplayConfigurations;
+
+// Describes the configuration of the whole desktop.
+struct RTC_EXPORT MacDesktopConfiguration {
+ // Used to request bottom-up or top-down coordinates.
+ enum Origin { BottomLeftOrigin, TopLeftOrigin };
+
+ MacDesktopConfiguration();
+ MacDesktopConfiguration(const MacDesktopConfiguration& other);
+ MacDesktopConfiguration(MacDesktopConfiguration&& other);
+ ~MacDesktopConfiguration();
+
+ MacDesktopConfiguration& operator=(const MacDesktopConfiguration& other);
+ MacDesktopConfiguration& operator=(MacDesktopConfiguration&& other);
+
+ // Returns the desktop & display configurations.
+ // If BottomLeftOrigin is used, the output is in Cocoa-style "bottom-up"
+ // (the origin is the bottom-left of the primary monitor, and coordinates
+ // increase as you move up the screen). Otherwise, the configuration will be
+ // converted to follow top-left coordinate system as Windows and X11.
+ static MacDesktopConfiguration GetCurrent(Origin origin);
+
+ // Returns true if the given desktop configuration equals this one.
+ bool Equals(const MacDesktopConfiguration& other);
+
+ // If `id` corresponds to the built-in display, return its configuration,
+ // otherwise return the configuration for the display with the specified id,
+ // or nullptr if no such display exists.
+ const MacDisplayConfiguration* FindDisplayConfigurationById(
+ CGDirectDisplayID id);
+
+ // Bounds of the desktop excluding monitors with DPI settings different from
+ // the main monitor. In Density-Independent Pixels (DIPs).
+ DesktopRect bounds;
+
+ // Same as bounds, but expressed in physical pixels.
+ DesktopRect pixel_bounds;
+
+ // Scale factor from DIPs to physical pixels.
+ float dip_to_pixel_scale = 1.0f;
+
+ // Configurations of the displays making up the desktop area.
+ MacDisplayConfigurations displays;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.mm b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.mm
new file mode 100644
index 0000000000..3b888c37c2
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration.mm
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/mac/desktop_configuration.h"
+
+#include <math.h>
+#include <algorithm>
+#include <Cocoa/Cocoa.h>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+namespace {
+
+DesktopRect NSRectToDesktopRect(const NSRect& ns_rect) {
+ return DesktopRect::MakeLTRB(
+ static_cast<int>(floor(ns_rect.origin.x)),
+ static_cast<int>(floor(ns_rect.origin.y)),
+ static_cast<int>(ceil(ns_rect.origin.x + ns_rect.size.width)),
+ static_cast<int>(ceil(ns_rect.origin.y + ns_rect.size.height)));
+}
+
+// Inverts the position of `rect` from bottom-up coordinates to top-down,
+// relative to `bounds`.
+void InvertRectYOrigin(const DesktopRect& bounds,
+ DesktopRect* rect) {
+ RTC_DCHECK_EQ(bounds.top(), 0);
+ *rect = DesktopRect::MakeXYWH(
+ rect->left(), bounds.bottom() - rect->bottom(),
+ rect->width(), rect->height());
+}
+
+MacDisplayConfiguration GetConfigurationForScreen(NSScreen* screen) {
+ MacDisplayConfiguration display_config;
+
+ // Fetch the NSScreenNumber, which is also the CGDirectDisplayID.
+ NSDictionary* device_description = [screen deviceDescription];
+ display_config.id = static_cast<CGDirectDisplayID>(
+ [[device_description objectForKey:@"NSScreenNumber"] intValue]);
+
+ // Determine the display's logical & physical dimensions.
+ NSRect ns_bounds = [screen frame];
+ display_config.bounds = NSRectToDesktopRect(ns_bounds);
+
+ // If the host is running Mac OS X 10.7+ or later, query the scaling factor
+ // between logical and physical (aka "backing") pixels, otherwise assume 1:1.
+ if ([screen respondsToSelector:@selector(backingScaleFactor)] &&
+ [screen respondsToSelector:@selector(convertRectToBacking:)]) {
+ display_config.dip_to_pixel_scale = [screen backingScaleFactor];
+ NSRect ns_pixel_bounds = [screen convertRectToBacking: ns_bounds];
+ display_config.pixel_bounds = NSRectToDesktopRect(ns_pixel_bounds);
+ } else {
+ display_config.pixel_bounds = display_config.bounds;
+ }
+
+ // Determine if the display is built-in or external.
+ display_config.is_builtin = CGDisplayIsBuiltin(display_config.id);
+
+ return display_config;
+}
+
+} // namespace
+
+MacDisplayConfiguration::MacDisplayConfiguration() = default;
+MacDisplayConfiguration::MacDisplayConfiguration(
+ const MacDisplayConfiguration& other) = default;
+MacDisplayConfiguration::MacDisplayConfiguration(
+ MacDisplayConfiguration&& other) = default;
+MacDisplayConfiguration::~MacDisplayConfiguration() = default;
+
+MacDisplayConfiguration& MacDisplayConfiguration::operator=(
+ const MacDisplayConfiguration& other) = default;
+MacDisplayConfiguration& MacDisplayConfiguration::operator=(
+ MacDisplayConfiguration&& other) = default;
+
+MacDesktopConfiguration::MacDesktopConfiguration() = default;
+MacDesktopConfiguration::MacDesktopConfiguration(
+ const MacDesktopConfiguration& other) = default;
+MacDesktopConfiguration::MacDesktopConfiguration(
+ MacDesktopConfiguration&& other) = default;
+MacDesktopConfiguration::~MacDesktopConfiguration() = default;
+
+MacDesktopConfiguration& MacDesktopConfiguration::operator=(
+ const MacDesktopConfiguration& other) = default;
+MacDesktopConfiguration& MacDesktopConfiguration::operator=(
+ MacDesktopConfiguration&& other) = default;
+
+// static
+MacDesktopConfiguration MacDesktopConfiguration::GetCurrent(Origin origin) {
+ MacDesktopConfiguration desktop_config;
+
+ NSArray* screens = [NSScreen screens];
+ RTC_DCHECK(screens);
+
+ // Iterator over the monitors, adding the primary monitor and monitors whose
+ // DPI match that of the primary monitor.
+ for (NSUInteger i = 0; i < [screens count]; ++i) {
+ MacDisplayConfiguration display_config =
+ GetConfigurationForScreen([screens objectAtIndex: i]);
+
+ if (i == 0)
+ desktop_config.dip_to_pixel_scale = display_config.dip_to_pixel_scale;
+
+ // Cocoa uses bottom-up coordinates, so if the caller wants top-down then
+ // we need to invert the positions of secondary monitors relative to the
+ // primary one (the primary monitor's position is (0,0) in both systems).
+ if (i > 0 && origin == TopLeftOrigin) {
+ InvertRectYOrigin(desktop_config.displays[0].bounds,
+ &display_config.bounds);
+ // `display_bounds` is density dependent, so we need to convert the
+ // primay monitor's position into the secondary monitor's density context.
+ float scaling_factor = display_config.dip_to_pixel_scale /
+ desktop_config.displays[0].dip_to_pixel_scale;
+ DesktopRect primary_bounds = DesktopRect::MakeLTRB(
+ desktop_config.displays[0].pixel_bounds.left() * scaling_factor,
+ desktop_config.displays[0].pixel_bounds.top() * scaling_factor,
+ desktop_config.displays[0].pixel_bounds.right() * scaling_factor,
+ desktop_config.displays[0].pixel_bounds.bottom() * scaling_factor);
+ InvertRectYOrigin(primary_bounds, &display_config.pixel_bounds);
+ }
+
+ // Add the display to the configuration.
+ desktop_config.displays.push_back(display_config);
+
+ // Update the desktop bounds to account for this display, unless the current
+ // display uses different DPI settings.
+ if (display_config.dip_to_pixel_scale ==
+ desktop_config.dip_to_pixel_scale) {
+ desktop_config.bounds.UnionWith(display_config.bounds);
+ desktop_config.pixel_bounds.UnionWith(display_config.pixel_bounds);
+ }
+ }
+
+ return desktop_config;
+}
+
+// For convenience of comparing MacDisplayConfigurations in
+// MacDesktopConfiguration::Equals.
+bool operator==(const MacDisplayConfiguration& left,
+ const MacDisplayConfiguration& right) {
+ return left.id == right.id &&
+ left.bounds.equals(right.bounds) &&
+ left.pixel_bounds.equals(right.pixel_bounds) &&
+ left.dip_to_pixel_scale == right.dip_to_pixel_scale;
+}
+
+bool MacDesktopConfiguration::Equals(const MacDesktopConfiguration& other) {
+ return bounds.equals(other.bounds) &&
+ pixel_bounds.equals(other.pixel_bounds) &&
+ dip_to_pixel_scale == other.dip_to_pixel_scale &&
+ displays == other.displays;
+}
+
+const MacDisplayConfiguration*
+MacDesktopConfiguration::FindDisplayConfigurationById(
+ CGDirectDisplayID id) {
+ bool is_builtin = CGDisplayIsBuiltin(id);
+ for (MacDisplayConfigurations::const_iterator it = displays.begin();
+ it != displays.end(); ++it) {
+ // The MBP having both discrete and integrated graphic cards will do
+ // automate graphics switching by default. When it switches from discrete to
+ // integrated one, the current display ID of the built-in display will
+ // change and this will cause screen capture stops.
+ // So make screen capture of built-in display continuing even if its display
+ // ID is changed.
+ if ((is_builtin && it->is_builtin) || (!is_builtin && it->id == id)) return &(*it);
+ }
+ return NULL;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc
new file mode 100644
index 0000000000..048a679ecc
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.cc
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/mac/desktop_configuration_monitor.h"
+
+#include "modules/desktop_capture/mac/desktop_configuration.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/trace_event.h"
+
+namespace webrtc {
+
+DesktopConfigurationMonitor::DesktopConfigurationMonitor() {
+ CGError err = CGDisplayRegisterReconfigurationCallback(
+ DesktopConfigurationMonitor::DisplaysReconfiguredCallback, this);
+ if (err != kCGErrorSuccess)
+ RTC_LOG(LS_ERROR) << "CGDisplayRegisterReconfigurationCallback " << err;
+ MutexLock lock(&desktop_configuration_lock_);
+ desktop_configuration_ = MacDesktopConfiguration::GetCurrent(
+ MacDesktopConfiguration::TopLeftOrigin);
+}
+
+DesktopConfigurationMonitor::~DesktopConfigurationMonitor() {
+ CGError err = CGDisplayRemoveReconfigurationCallback(
+ DesktopConfigurationMonitor::DisplaysReconfiguredCallback, this);
+ if (err != kCGErrorSuccess)
+ RTC_LOG(LS_ERROR) << "CGDisplayRemoveReconfigurationCallback " << err;
+}
+
+MacDesktopConfiguration DesktopConfigurationMonitor::desktop_configuration() {
+ MutexLock lock(&desktop_configuration_lock_);
+ return desktop_configuration_;
+}
+
+// static
+// This method may be called on any system thread.
+void DesktopConfigurationMonitor::DisplaysReconfiguredCallback(
+ CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags,
+ void* user_parameter) {
+ DesktopConfigurationMonitor* monitor =
+ reinterpret_cast<DesktopConfigurationMonitor*>(user_parameter);
+ monitor->DisplaysReconfigured(display, flags);
+}
+
+void DesktopConfigurationMonitor::DisplaysReconfigured(
+ CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags) {
+ TRACE_EVENT0("webrtc", "DesktopConfigurationMonitor::DisplaysReconfigured");
+ RTC_LOG(LS_INFO) << "DisplaysReconfigured: "
+ "DisplayID "
+ << display << "; ChangeSummaryFlags " << flags;
+
+ if (flags & kCGDisplayBeginConfigurationFlag) {
+ reconfiguring_displays_.insert(display);
+ return;
+ }
+
+ reconfiguring_displays_.erase(display);
+ if (reconfiguring_displays_.empty()) {
+ MutexLock lock(&desktop_configuration_lock_);
+ desktop_configuration_ = MacDesktopConfiguration::GetCurrent(
+ MacDesktopConfiguration::TopLeftOrigin);
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h
new file mode 100644
index 0000000000..747295a538
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_configuration_monitor.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include <memory>
+#include <set>
+
+#include "api/ref_counted_base.h"
+#include "modules/desktop_capture/mac/desktop_configuration.h"
+#include "rtc_base/synchronization/mutex.h"
+
+namespace webrtc {
+
+// The class provides functions to synchronize capturing and display
+// reconfiguring across threads, and the up-to-date MacDesktopConfiguration.
+class DesktopConfigurationMonitor final
+ : public rtc::RefCountedNonVirtual<DesktopConfigurationMonitor> {
+ public:
+ DesktopConfigurationMonitor();
+ ~DesktopConfigurationMonitor();
+
+ DesktopConfigurationMonitor(const DesktopConfigurationMonitor&) = delete;
+ DesktopConfigurationMonitor& operator=(const DesktopConfigurationMonitor&) =
+ delete;
+
+ // Returns the current desktop configuration.
+ MacDesktopConfiguration desktop_configuration();
+
+ private:
+ static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags,
+ void* user_parameter);
+ void DisplaysReconfigured(CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags);
+
+ Mutex desktop_configuration_lock_;
+ MacDesktopConfiguration desktop_configuration_
+ RTC_GUARDED_BY(&desktop_configuration_lock_);
+ std::set<CGDirectDisplayID> reconfiguring_displays_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_CONFIGURATION_MONITOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.h b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.h
new file mode 100644
index 0000000000..d6279f9b36
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_CGIMAGE_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_CGIMAGE_H_
+
+#include <CoreGraphics/CoreGraphics.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "sdk/objc/helpers/scoped_cftyperef.h"
+
+namespace webrtc {
+
+class DesktopFrameCGImage final : public DesktopFrame {
+ public:
+ // Create an image containing a snapshot of the display at the time this is
+ // being called.
+ static std::unique_ptr<DesktopFrameCGImage> CreateForDisplay(
+ CGDirectDisplayID display_id);
+
+ // Create an image containing a snaphot of the given window at the time this
+ // is being called. This also works when the window is overlapped or in
+ // another workspace.
+ static std::unique_ptr<DesktopFrameCGImage> CreateForWindow(
+ CGWindowID window_id);
+
+ ~DesktopFrameCGImage() override;
+
+ DesktopFrameCGImage(const DesktopFrameCGImage&) = delete;
+ DesktopFrameCGImage& operator=(const DesktopFrameCGImage&) = delete;
+
+ private:
+ static std::unique_ptr<DesktopFrameCGImage> CreateFromCGImage(
+ rtc::ScopedCFTypeRef<CGImageRef> cg_image);
+
+ // This constructor expects `cg_image` to hold a non-null CGImageRef.
+ DesktopFrameCGImage(DesktopSize size,
+ int stride,
+ uint8_t* data,
+ rtc::ScopedCFTypeRef<CGImageRef> cg_image,
+ rtc::ScopedCFTypeRef<CFDataRef> cg_data);
+
+ const rtc::ScopedCFTypeRef<CGImageRef> cg_image_;
+ const rtc::ScopedCFTypeRef<CFDataRef> cg_data_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_CGIMAGE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.mm b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.mm
new file mode 100644
index 0000000000..0fb69b272d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_cgimage.mm
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/mac/desktop_frame_cgimage.h"
+
+#include <AvailabilityMacros.h>
+
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+// static
+std::unique_ptr<DesktopFrameCGImage> DesktopFrameCGImage::CreateForDisplay(
+ CGDirectDisplayID display_id) {
+ // Create an image containing a snapshot of the display.
+ rtc::ScopedCFTypeRef<CGImageRef> cg_image(CGDisplayCreateImage(display_id));
+ if (!cg_image) {
+ return nullptr;
+ }
+
+ return DesktopFrameCGImage::CreateFromCGImage(cg_image);
+}
+
+// static
+std::unique_ptr<DesktopFrameCGImage> DesktopFrameCGImage::CreateForWindow(CGWindowID window_id) {
+ rtc::ScopedCFTypeRef<CGImageRef> cg_image(
+ CGWindowListCreateImage(CGRectNull,
+ kCGWindowListOptionIncludingWindow,
+ window_id,
+ kCGWindowImageBoundsIgnoreFraming));
+ if (!cg_image) {
+ return nullptr;
+ }
+
+ return DesktopFrameCGImage::CreateFromCGImage(cg_image);
+}
+
+// static
+std::unique_ptr<DesktopFrameCGImage> DesktopFrameCGImage::CreateFromCGImage(
+ rtc::ScopedCFTypeRef<CGImageRef> cg_image) {
+ // Verify that the image has 32-bit depth.
+ int bits_per_pixel = CGImageGetBitsPerPixel(cg_image.get());
+ if (bits_per_pixel / 8 != DesktopFrame::kBytesPerPixel) {
+ RTC_LOG(LS_ERROR) << "CGDisplayCreateImage() returned imaged with " << bits_per_pixel
+ << " bits per pixel. Only 32-bit depth is supported.";
+ return nullptr;
+ }
+
+ // Request access to the raw pixel data via the image's DataProvider.
+ CGDataProviderRef cg_provider = CGImageGetDataProvider(cg_image.get());
+ RTC_DCHECK(cg_provider);
+
+ // CGDataProviderCopyData returns a new data object containing a copy of the provider’s
+ // data.
+ rtc::ScopedCFTypeRef<CFDataRef> cg_data(CGDataProviderCopyData(cg_provider));
+ RTC_DCHECK(cg_data);
+
+ // CFDataGetBytePtr returns a read-only pointer to the bytes of a CFData object.
+ uint8_t* data = const_cast<uint8_t*>(CFDataGetBytePtr(cg_data.get()));
+ RTC_DCHECK(data);
+
+ DesktopSize size(CGImageGetWidth(cg_image.get()), CGImageGetHeight(cg_image.get()));
+ int stride = CGImageGetBytesPerRow(cg_image.get());
+
+ std::unique_ptr<DesktopFrameCGImage> frame(
+ new DesktopFrameCGImage(size, stride, data, cg_image, cg_data));
+
+ CGColorSpaceRef cg_color_space = CGImageGetColorSpace(cg_image.get());
+ if (cg_color_space) {
+#if !defined(MAC_OS_X_VERSION_10_13) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_13
+ rtc::ScopedCFTypeRef<CFDataRef> cf_icc_profile(CGColorSpaceCopyICCProfile(cg_color_space));
+#else
+ rtc::ScopedCFTypeRef<CFDataRef> cf_icc_profile(CGColorSpaceCopyICCData(cg_color_space));
+#endif
+ if (cf_icc_profile) {
+ const uint8_t* data_as_byte =
+ reinterpret_cast<const uint8_t*>(CFDataGetBytePtr(cf_icc_profile.get()));
+ const size_t data_size = CFDataGetLength(cf_icc_profile.get());
+ if (data_as_byte && data_size > 0) {
+ frame->set_icc_profile(std::vector<uint8_t>(data_as_byte, data_as_byte + data_size));
+ }
+ }
+ }
+
+ return frame;
+}
+
+DesktopFrameCGImage::DesktopFrameCGImage(DesktopSize size,
+ int stride,
+ uint8_t* data,
+ rtc::ScopedCFTypeRef<CGImageRef> cg_image,
+ rtc::ScopedCFTypeRef<CFDataRef> cg_data)
+ : DesktopFrame(size, stride, data, nullptr), cg_image_(cg_image), cg_data_(cg_data) {
+ RTC_DCHECK(cg_image_);
+ RTC_DCHECK(cg_data_);
+}
+
+DesktopFrameCGImage::~DesktopFrameCGImage() = default;
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.h b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.h
new file mode 100644
index 0000000000..73da0f693c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_IOSURFACE_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_IOSURFACE_H_
+
+#include <CoreGraphics/CoreGraphics.h>
+#include <IOSurface/IOSurface.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "sdk/objc/helpers/scoped_cftyperef.h"
+
+namespace webrtc {
+
+class DesktopFrameIOSurface final : public DesktopFrame {
+ public:
+ // Lock an IOSurfaceRef containing a snapshot of a display. Return NULL if
+ // failed to lock.
+ static std::unique_ptr<DesktopFrameIOSurface> Wrap(
+ rtc::ScopedCFTypeRef<IOSurfaceRef> io_surface);
+
+ ~DesktopFrameIOSurface() override;
+
+ DesktopFrameIOSurface(const DesktopFrameIOSurface&) = delete;
+ DesktopFrameIOSurface& operator=(const DesktopFrameIOSurface&) = delete;
+
+ private:
+ // This constructor expects `io_surface` to hold a non-null IOSurfaceRef.
+ explicit DesktopFrameIOSurface(rtc::ScopedCFTypeRef<IOSurfaceRef> io_surface);
+
+ const rtc::ScopedCFTypeRef<IOSurfaceRef> io_surface_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_IOSURFACE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.mm b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.mm
new file mode 100644
index 0000000000..b59b319db9
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_iosurface.mm
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/mac/desktop_frame_iosurface.h"
+
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+// static
+std::unique_ptr<DesktopFrameIOSurface> DesktopFrameIOSurface::Wrap(
+ rtc::ScopedCFTypeRef<IOSurfaceRef> io_surface) {
+ if (!io_surface) {
+ return nullptr;
+ }
+
+ IOSurfaceIncrementUseCount(io_surface.get());
+ IOReturn status = IOSurfaceLock(io_surface.get(), kIOSurfaceLockReadOnly, nullptr);
+ if (status != kIOReturnSuccess) {
+ RTC_LOG(LS_ERROR) << "Failed to lock the IOSurface with status " << status;
+ IOSurfaceDecrementUseCount(io_surface.get());
+ return nullptr;
+ }
+
+ // Verify that the image has 32-bit depth.
+ int bytes_per_pixel = IOSurfaceGetBytesPerElement(io_surface.get());
+ if (bytes_per_pixel != DesktopFrame::kBytesPerPixel) {
+ RTC_LOG(LS_ERROR) << "CGDisplayStream handler returned IOSurface with " << (8 * bytes_per_pixel)
+ << " bits per pixel. Only 32-bit depth is supported.";
+ IOSurfaceUnlock(io_surface.get(), kIOSurfaceLockReadOnly, nullptr);
+ IOSurfaceDecrementUseCount(io_surface.get());
+ return nullptr;
+ }
+
+ return std::unique_ptr<DesktopFrameIOSurface>(new DesktopFrameIOSurface(io_surface));
+}
+
+DesktopFrameIOSurface::DesktopFrameIOSurface(rtc::ScopedCFTypeRef<IOSurfaceRef> io_surface)
+ : DesktopFrame(
+ DesktopSize(IOSurfaceGetWidth(io_surface.get()), IOSurfaceGetHeight(io_surface.get())),
+ IOSurfaceGetBytesPerRow(io_surface.get()),
+ static_cast<uint8_t*>(IOSurfaceGetBaseAddress(io_surface.get())),
+ nullptr),
+ io_surface_(io_surface) {
+ RTC_DCHECK(io_surface_);
+}
+
+DesktopFrameIOSurface::~DesktopFrameIOSurface() {
+ IOSurfaceUnlock(io_surface_.get(), kIOSurfaceLockReadOnly, nullptr);
+ IOSurfaceDecrementUseCount(io_surface_.get());
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.h b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.h
new file mode 100644
index 0000000000..aad28d2f30
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_PROVIDER_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_PROVIDER_H_
+
+#include <CoreGraphics/CoreGraphics.h>
+#include <IOSurface/IOSurface.h>
+
+#include <map>
+#include <memory>
+
+#include "api/sequence_checker.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "sdk/objc/helpers/scoped_cftyperef.h"
+
+namespace webrtc {
+
+class DesktopFrameProvider {
+ public:
+ explicit DesktopFrameProvider(bool allow_iosurface);
+ ~DesktopFrameProvider();
+
+ DesktopFrameProvider(const DesktopFrameProvider&) = delete;
+ DesktopFrameProvider& operator=(const DesktopFrameProvider&) = delete;
+
+ // The caller takes ownership of the returned desktop frame. Otherwise
+ // returns null if `display_id` is invalid or not ready. Note that this
+ // function does not remove the frame from the internal container. Caller
+ // has to call the Release function.
+ std::unique_ptr<DesktopFrame> TakeLatestFrameForDisplay(
+ CGDirectDisplayID display_id);
+
+ // OS sends the latest IOSurfaceRef through
+ // CGDisplayStreamFrameAvailableHandler callback; we store it here.
+ void InvalidateIOSurface(CGDirectDisplayID display_id,
+ rtc::ScopedCFTypeRef<IOSurfaceRef> io_surface);
+
+ // Expected to be called before stopping the CGDisplayStreamRef streams.
+ void Release();
+
+ private:
+ SequenceChecker thread_checker_;
+ const bool allow_iosurface_;
+
+ // Most recent IOSurface that contains a capture of matching display.
+ std::map<CGDirectDisplayID, std::unique_ptr<SharedDesktopFrame>> io_surfaces_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_DESKTOP_FRAME_PROVIDER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.mm b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.mm
new file mode 100644
index 0000000000..009504a22b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/desktop_frame_provider.mm
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/mac/desktop_frame_provider.h"
+
+#include <utility>
+
+#include "modules/desktop_capture/mac/desktop_frame_cgimage.h"
+#include "modules/desktop_capture/mac/desktop_frame_iosurface.h"
+
+namespace webrtc {
+
+DesktopFrameProvider::DesktopFrameProvider(bool allow_iosurface)
+ : allow_iosurface_(allow_iosurface) {
+ thread_checker_.Detach();
+}
+
+DesktopFrameProvider::~DesktopFrameProvider() {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+
+ Release();
+}
+
+std::unique_ptr<DesktopFrame> DesktopFrameProvider::TakeLatestFrameForDisplay(
+ CGDirectDisplayID display_id) {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+
+ if (!allow_iosurface_ || !io_surfaces_[display_id]) {
+ // Regenerate a snapshot. If iosurface is on it will be empty until the
+ // stream handler is called.
+ return DesktopFrameCGImage::CreateForDisplay(display_id);
+ }
+
+ return io_surfaces_[display_id]->Share();
+}
+
+void DesktopFrameProvider::InvalidateIOSurface(CGDirectDisplayID display_id,
+ rtc::ScopedCFTypeRef<IOSurfaceRef> io_surface) {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+
+ if (!allow_iosurface_) {
+ return;
+ }
+
+ std::unique_ptr<DesktopFrameIOSurface> desktop_frame_iosurface =
+ DesktopFrameIOSurface::Wrap(io_surface);
+
+ io_surfaces_[display_id] = desktop_frame_iosurface ?
+ SharedDesktopFrame::Wrap(std::move(desktop_frame_iosurface)) :
+ nullptr;
+}
+
+void DesktopFrameProvider::Release() {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+
+ if (!allow_iosurface_) {
+ return;
+ }
+
+ io_surfaces_.clear();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.cc b/third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.cc
new file mode 100644
index 0000000000..45cd3223d2
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.cc
@@ -0,0 +1,238 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/mac/full_screen_mac_application_handler.h"
+
+#include <libproc.h>
+
+#include <algorithm>
+#include <functional>
+#include <string>
+
+#include "absl/strings/match.h"
+#include "absl/strings/string_view.h"
+#include "api/function_view.h"
+#include "modules/desktop_capture/mac/window_list_utils.h"
+
+namespace webrtc {
+namespace {
+
+static constexpr const char* kPowerPointSlideShowTitles[] = {
+ "PowerPoint-Bildschirmpräsentation",
+ "Προβολή παρουσίασης PowerPoint",
+ "PowerPoint スライド ショー",
+ "PowerPoint Slide Show",
+ "PowerPoint 幻灯片放映",
+ "Presentación de PowerPoint",
+ "PowerPoint-slideshow",
+ "Presentazione di PowerPoint",
+ "Prezentácia programu PowerPoint",
+ "Apresentação do PowerPoint",
+ "PowerPoint-bildspel",
+ "Prezentace v aplikaci PowerPoint",
+ "PowerPoint 슬라이드 쇼",
+ "PowerPoint-lysbildefremvisning",
+ "PowerPoint-vetítés",
+ "PowerPoint Slayt Gösterisi",
+ "Pokaz slajdów programu PowerPoint",
+ "PowerPoint 投影片放映",
+ "Демонстрация PowerPoint",
+ "Diaporama PowerPoint",
+ "PowerPoint-diaesitys",
+ "Peragaan Slide PowerPoint",
+ "PowerPoint-diavoorstelling",
+ "การนำเสนอสไลด์ PowerPoint",
+ "Apresentação de slides do PowerPoint",
+ "הצגת שקופיות של PowerPoint",
+ "عرض شرائح في PowerPoint"};
+
+class FullScreenMacApplicationHandler : public FullScreenApplicationHandler {
+ public:
+ using TitlePredicate =
+ std::function<bool(absl::string_view, absl::string_view)>;
+
+ FullScreenMacApplicationHandler(DesktopCapturer::SourceId sourceId,
+ TitlePredicate title_predicate,
+ bool ignore_original_window)
+ : FullScreenApplicationHandler(sourceId),
+ title_predicate_(title_predicate),
+ owner_pid_(GetWindowOwnerPid(sourceId)),
+ ignore_original_window_(ignore_original_window) {}
+
+ protected:
+ using CachePredicate =
+ rtc::FunctionView<bool(const DesktopCapturer::Source&)>;
+
+ void InvalidateCacheIfNeeded(const DesktopCapturer::SourceList& source_list,
+ int64_t timestamp,
+ CachePredicate predicate) const {
+ if (timestamp != cache_timestamp_) {
+ cache_sources_.clear();
+ std::copy_if(source_list.begin(), source_list.end(),
+ std::back_inserter(cache_sources_), predicate);
+ cache_timestamp_ = timestamp;
+ }
+ }
+
+ WindowId FindFullScreenWindowWithSamePid(
+ const DesktopCapturer::SourceList& source_list,
+ int64_t timestamp) const {
+ InvalidateCacheIfNeeded(source_list, timestamp,
+ [&](const DesktopCapturer::Source& src) {
+ return src.id != GetSourceId() &&
+ GetWindowOwnerPid(src.id) == owner_pid_;
+ });
+ if (cache_sources_.empty())
+ return kCGNullWindowID;
+
+ const auto original_window = GetSourceId();
+ const std::string title = GetWindowTitle(original_window);
+
+ // We can ignore any windows with empty titles cause regardless type of
+ // application it's impossible to verify that full screen window and
+ // original window are related to the same document.
+ if (title.empty())
+ return kCGNullWindowID;
+
+ MacDesktopConfiguration desktop_config =
+ MacDesktopConfiguration::GetCurrent(
+ MacDesktopConfiguration::TopLeftOrigin);
+
+ const auto it = std::find_if(
+ cache_sources_.begin(), cache_sources_.end(),
+ [&](const DesktopCapturer::Source& src) {
+ const std::string window_title = GetWindowTitle(src.id);
+
+ if (window_title.empty())
+ return false;
+
+ if (title_predicate_ && !title_predicate_(title, window_title))
+ return false;
+
+ return IsWindowFullScreen(desktop_config, src.id);
+ });
+
+ return it != cache_sources_.end() ? it->id : 0;
+ }
+
+ DesktopCapturer::SourceId FindFullScreenWindow(
+ const DesktopCapturer::SourceList& source_list,
+ int64_t timestamp) const override {
+ return !ignore_original_window_ && IsWindowOnScreen(GetSourceId())
+ ? 0
+ : FindFullScreenWindowWithSamePid(source_list, timestamp);
+ }
+
+ protected:
+ const TitlePredicate title_predicate_;
+ const int owner_pid_;
+ const bool ignore_original_window_;
+ mutable int64_t cache_timestamp_ = 0;
+ mutable DesktopCapturer::SourceList cache_sources_;
+};
+
+bool equal_title_predicate(absl::string_view original_title,
+ absl::string_view title) {
+ return original_title == title;
+}
+
+bool slide_show_title_predicate(absl::string_view original_title,
+ absl::string_view title) {
+ if (title.find(original_title) == absl::string_view::npos)
+ return false;
+
+ for (const char* pp_slide_title : kPowerPointSlideShowTitles) {
+ if (absl::StartsWith(title, pp_slide_title))
+ return true;
+ }
+ return false;
+}
+
+class OpenOfficeApplicationHandler : public FullScreenMacApplicationHandler {
+ public:
+ OpenOfficeApplicationHandler(DesktopCapturer::SourceId sourceId)
+ : FullScreenMacApplicationHandler(sourceId, nullptr, false) {}
+
+ DesktopCapturer::SourceId FindFullScreenWindow(
+ const DesktopCapturer::SourceList& source_list,
+ int64_t timestamp) const override {
+ InvalidateCacheIfNeeded(source_list, timestamp,
+ [&](const DesktopCapturer::Source& src) {
+ return GetWindowOwnerPid(src.id) == owner_pid_;
+ });
+
+ const auto original_window = GetSourceId();
+ const std::string original_title = GetWindowTitle(original_window);
+
+ // Check if we have only one document window, otherwise it's not possible
+ // to securely match a document window and a slide show window which has
+ // empty title.
+ if (std::any_of(cache_sources_.begin(), cache_sources_.end(),
+ [&original_title](const DesktopCapturer::Source& src) {
+ return src.title.length() && src.title != original_title;
+ })) {
+ return kCGNullWindowID;
+ }
+
+ MacDesktopConfiguration desktop_config =
+ MacDesktopConfiguration::GetCurrent(
+ MacDesktopConfiguration::TopLeftOrigin);
+
+ // Looking for slide show window,
+ // it must be a full screen window with empty title
+ const auto slide_show_window = std::find_if(
+ cache_sources_.begin(), cache_sources_.end(), [&](const auto& src) {
+ return src.title.empty() &&
+ IsWindowFullScreen(desktop_config, src.id);
+ });
+
+ if (slide_show_window == cache_sources_.end()) {
+ return kCGNullWindowID;
+ }
+
+ return slide_show_window->id;
+ }
+};
+
+} // namespace
+
+std::unique_ptr<FullScreenApplicationHandler>
+CreateFullScreenMacApplicationHandler(DesktopCapturer::SourceId sourceId) {
+ std::unique_ptr<FullScreenApplicationHandler> result;
+ int pid = GetWindowOwnerPid(sourceId);
+ char buffer[PROC_PIDPATHINFO_MAXSIZE];
+ int path_length = proc_pidpath(pid, buffer, sizeof(buffer));
+ if (path_length > 0) {
+ const char* last_slash = strrchr(buffer, '/');
+ const std::string name{last_slash ? last_slash + 1 : buffer};
+ const std::string owner_name = GetWindowOwnerName(sourceId);
+ FullScreenMacApplicationHandler::TitlePredicate predicate = nullptr;
+ bool ignore_original_window = false;
+ if (name.find("Google Chrome") == 0 || name == "Chromium") {
+ predicate = equal_title_predicate;
+ } else if (name == "Microsoft PowerPoint") {
+ predicate = slide_show_title_predicate;
+ ignore_original_window = true;
+ } else if (name == "Keynote") {
+ predicate = equal_title_predicate;
+ } else if (owner_name == "OpenOffice") {
+ return std::make_unique<OpenOfficeApplicationHandler>(sourceId);
+ }
+
+ if (predicate) {
+ result.reset(new FullScreenMacApplicationHandler(sourceId, predicate,
+ ignore_original_window));
+ }
+ }
+
+ return result;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.h b/third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.h
new file mode 100644
index 0000000000..f795a22030
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/full_screen_mac_application_handler.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_FULL_SCREEN_MAC_APPLICATION_HANDLER_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_FULL_SCREEN_MAC_APPLICATION_HANDLER_H_
+
+#include <memory>
+#include "modules/desktop_capture/full_screen_application_handler.h"
+
+namespace webrtc {
+
+std::unique_ptr<FullScreenApplicationHandler>
+CreateFullScreenMacApplicationHandler(DesktopCapturer::SourceId sourceId);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_FULL_SCREEN_MAC_APPLICATION_HANDLER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.h b/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.h
new file mode 100644
index 0000000000..7be05cc639
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_SCREEN_CAPTURER_MAC_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_SCREEN_CAPTURER_MAC_H_
+
+#include <CoreGraphics/CoreGraphics.h>
+
+#include <memory>
+#include <vector>
+
+#include "api/sequence_checker.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/mac/desktop_configuration.h"
+#include "modules/desktop_capture/mac/desktop_configuration_monitor.h"
+#include "modules/desktop_capture/mac/desktop_frame_provider.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/screen_capturer_helper.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+
+namespace webrtc {
+
+class DisplayStreamManager;
+
+// A class to perform video frame capturing for mac.
+class ScreenCapturerMac final : public DesktopCapturer {
+ public:
+ ScreenCapturerMac(
+ rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor,
+ bool detect_updated_region,
+ bool allow_iosurface);
+ ~ScreenCapturerMac() override;
+
+ ScreenCapturerMac(const ScreenCapturerMac&) = delete;
+ ScreenCapturerMac& operator=(const ScreenCapturerMac&) = delete;
+
+ // TODO(julien.isorce): Remove Init() or make it private.
+ bool Init();
+
+ // DesktopCapturer interface.
+ void Start(Callback* callback) override;
+ void CaptureFrame() override;
+ void SetExcludedWindow(WindowId window) override;
+ bool GetSourceList(SourceList* screens) override;
+ bool SelectSource(SourceId id) override;
+
+ private:
+ // Returns false if the selected screen is no longer valid.
+ bool CgBlit(const DesktopFrame& frame, const DesktopRegion& region);
+
+ // Called when the screen configuration is changed.
+ void ScreenConfigurationChanged();
+
+ bool RegisterRefreshAndMoveHandlers();
+ void UnregisterRefreshAndMoveHandlers();
+
+ void ScreenRefresh(CGDirectDisplayID display_id,
+ CGRectCount count,
+ const CGRect* rect_array,
+ DesktopVector display_origin,
+ IOSurfaceRef io_surface);
+ void ReleaseBuffers();
+
+ std::unique_ptr<DesktopFrame> CreateFrame();
+
+ const bool detect_updated_region_;
+
+ Callback* callback_ = nullptr;
+
+ // Queue of the frames buffers.
+ ScreenCaptureFrameQueue<SharedDesktopFrame> queue_;
+
+ // Current display configuration.
+ MacDesktopConfiguration desktop_config_;
+
+ // Currently selected display, or 0 if the full desktop is selected. On OS X
+ // 10.6 and before, this is always 0.
+ CGDirectDisplayID current_display_ = 0;
+
+ // The physical pixel bounds of the current screen.
+ DesktopRect screen_pixel_bounds_;
+
+ // The dip to physical pixel scale of the current screen.
+ float dip_to_pixel_scale_ = 1.0f;
+
+ // A thread-safe list of invalid rectangles, and the size of the most
+ // recently captured screen.
+ ScreenCapturerHelper helper_;
+
+ // Contains an invalid region from the previous capture.
+ DesktopRegion last_invalid_region_;
+
+ // Monitoring display reconfiguration.
+ rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor_;
+
+ CGWindowID excluded_window_ = 0;
+
+ // List of streams, one per screen.
+ std::vector<CGDisplayStreamRef> display_streams_;
+
+ // Container holding latest state of the snapshot per displays.
+ DesktopFrameProvider desktop_frame_provider_;
+
+ // Start, CaptureFrame and destructor have to called in the same thread.
+ SequenceChecker thread_checker_;
+
+ // Used to force CaptureFrame to update it's screen configuration
+ // and reregister event handlers. This ensure that this
+ // occurs on the ScreenCapture thread. Read and written from
+ // both the VideoCapture thread and ScreenCapture thread.
+ // Protected by desktop_config_monitor_.
+ bool update_screen_configuration_ = false;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_SCREEN_CAPTURER_MAC_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.mm b/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.mm
new file mode 100644
index 0000000000..cad0c5b65b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/screen_capturer_mac.mm
@@ -0,0 +1,552 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <utility>
+
+#include "modules/desktop_capture/mac/screen_capturer_mac.h"
+
+#include "modules/desktop_capture/mac/desktop_frame_provider.h"
+#include "modules/desktop_capture/mac/window_list_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/trace_event.h"
+#include "sdk/objc/helpers/scoped_cftyperef.h"
+
+namespace webrtc {
+
+namespace {
+
+// Scales all coordinates of a rect by a specified factor.
+DesktopRect ScaleAndRoundCGRect(const CGRect& rect, float scale) {
+ return DesktopRect::MakeLTRB(static_cast<int>(floor(rect.origin.x * scale)),
+ static_cast<int>(floor(rect.origin.y * scale)),
+ static_cast<int>(ceil((rect.origin.x + rect.size.width) * scale)),
+ static_cast<int>(ceil((rect.origin.y + rect.size.height) * scale)));
+}
+
+// Copy pixels in the `rect` from `src_place` to `dest_plane`. `rect` should be
+// relative to the origin of `src_plane` and `dest_plane`.
+void CopyRect(const uint8_t* src_plane,
+ int src_plane_stride,
+ uint8_t* dest_plane,
+ int dest_plane_stride,
+ int bytes_per_pixel,
+ const DesktopRect& rect) {
+ // Get the address of the starting point.
+ const int src_y_offset = src_plane_stride * rect.top();
+ const int dest_y_offset = dest_plane_stride * rect.top();
+ const int x_offset = bytes_per_pixel * rect.left();
+ src_plane += src_y_offset + x_offset;
+ dest_plane += dest_y_offset + x_offset;
+
+ // Copy pixels in the rectangle line by line.
+ const int bytes_per_line = bytes_per_pixel * rect.width();
+ const int height = rect.height();
+ for (int i = 0; i < height; ++i) {
+ memcpy(dest_plane, src_plane, bytes_per_line);
+ src_plane += src_plane_stride;
+ dest_plane += dest_plane_stride;
+ }
+}
+
+// Returns an array of CGWindowID for all the on-screen windows except
+// `window_to_exclude`, or NULL if the window is not found or it fails. The
+// caller should release the returned CFArrayRef.
+CFArrayRef CreateWindowListWithExclusion(CGWindowID window_to_exclude) {
+ if (!window_to_exclude) return nullptr;
+
+ CFArrayRef all_windows =
+ CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
+ if (!all_windows) return nullptr;
+
+ CFMutableArrayRef returned_array =
+ CFArrayCreateMutable(nullptr, CFArrayGetCount(all_windows), nullptr);
+
+ bool found = false;
+ for (CFIndex i = 0; i < CFArrayGetCount(all_windows); ++i) {
+ CFDictionaryRef window =
+ reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(all_windows, i));
+
+ CGWindowID id = GetWindowId(window);
+ if (id == window_to_exclude) {
+ found = true;
+ continue;
+ }
+ CFArrayAppendValue(returned_array, reinterpret_cast<void*>(id));
+ }
+ CFRelease(all_windows);
+
+ if (!found) {
+ CFRelease(returned_array);
+ returned_array = nullptr;
+ }
+ return returned_array;
+}
+
+// Returns the bounds of `window` in physical pixels, enlarged by a small amount
+// on four edges to take account of the border/shadow effects.
+DesktopRect GetExcludedWindowPixelBounds(CGWindowID window, float dip_to_pixel_scale) {
+ // The amount of pixels to add to the actual window bounds to take into
+ // account of the border/shadow effects.
+ static const int kBorderEffectSize = 20;
+ CGRect rect;
+ CGWindowID ids[1];
+ ids[0] = window;
+
+ CFArrayRef window_id_array =
+ CFArrayCreate(nullptr, reinterpret_cast<const void**>(&ids), 1, nullptr);
+ CFArrayRef window_array = CGWindowListCreateDescriptionFromArray(window_id_array);
+
+ if (CFArrayGetCount(window_array) > 0) {
+ CFDictionaryRef win =
+ reinterpret_cast<CFDictionaryRef>(CFArrayGetValueAtIndex(window_array, 0));
+ CFDictionaryRef bounds_ref =
+ reinterpret_cast<CFDictionaryRef>(CFDictionaryGetValue(win, kCGWindowBounds));
+ CGRectMakeWithDictionaryRepresentation(bounds_ref, &rect);
+ }
+
+ CFRelease(window_id_array);
+ CFRelease(window_array);
+
+ rect.origin.x -= kBorderEffectSize;
+ rect.origin.y -= kBorderEffectSize;
+ rect.size.width += kBorderEffectSize * 2;
+ rect.size.height += kBorderEffectSize * 2;
+ // `rect` is in DIP, so convert to physical pixels.
+ return ScaleAndRoundCGRect(rect, dip_to_pixel_scale);
+}
+
+// Create an image of the given region using the given `window_list`.
+// `pixel_bounds` should be in the primary display's coordinate in physical
+// pixels.
+rtc::ScopedCFTypeRef<CGImageRef> CreateExcludedWindowRegionImage(const DesktopRect& pixel_bounds,
+ float dip_to_pixel_scale,
+ CFArrayRef window_list) {
+ CGRect window_bounds;
+ // The origin is in DIP while the size is in physical pixels. That's what
+ // CGWindowListCreateImageFromArray expects.
+ window_bounds.origin.x = pixel_bounds.left() / dip_to_pixel_scale;
+ window_bounds.origin.y = pixel_bounds.top() / dip_to_pixel_scale;
+ window_bounds.size.width = pixel_bounds.width();
+ window_bounds.size.height = pixel_bounds.height();
+
+ return rtc::ScopedCFTypeRef<CGImageRef>(
+ CGWindowListCreateImageFromArray(window_bounds, window_list, kCGWindowImageDefault));
+}
+
+} // namespace
+
+ScreenCapturerMac::ScreenCapturerMac(
+ rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor,
+ bool detect_updated_region,
+ bool allow_iosurface)
+ : detect_updated_region_(detect_updated_region),
+ desktop_config_monitor_(desktop_config_monitor),
+ desktop_frame_provider_(allow_iosurface) {
+ RTC_LOG(LS_INFO) << "Allow IOSurface: " << allow_iosurface;
+ thread_checker_.Detach();
+}
+
+ScreenCapturerMac::~ScreenCapturerMac() {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+ ReleaseBuffers();
+ UnregisterRefreshAndMoveHandlers();
+}
+
+bool ScreenCapturerMac::Init() {
+ TRACE_EVENT0("webrtc", "ScreenCapturerMac::Init");
+ desktop_config_ = desktop_config_monitor_->desktop_configuration();
+ return true;
+}
+
+void ScreenCapturerMac::ReleaseBuffers() {
+ // The buffers might be in use by the encoder, so don't delete them here.
+ // Instead, mark them as "needs update"; next time the buffers are used by
+ // the capturer, they will be recreated if necessary.
+ queue_.Reset();
+}
+
+void ScreenCapturerMac::Start(Callback* callback) {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+ TRACE_EVENT_INSTANT1(
+ "webrtc", "ScreenCapturermac::Start", "target display id ", current_display_);
+
+ callback_ = callback;
+ update_screen_configuration_ = false;
+ // Start and operate CGDisplayStream handler all from capture thread.
+ if (!RegisterRefreshAndMoveHandlers()) {
+ RTC_LOG(LS_ERROR) << "Failed to register refresh and move handlers.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+ ScreenConfigurationChanged();
+}
+
+void ScreenCapturerMac::CaptureFrame() {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+ TRACE_EVENT0("webrtc", "creenCapturerMac::CaptureFrame");
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ queue_.MoveToNextFrame();
+ if (queue_.current_frame() && queue_.current_frame()->IsShared()) {
+ RTC_DLOG(LS_WARNING) << "Overwriting frame that is still shared.";
+ }
+
+ MacDesktopConfiguration new_config = desktop_config_monitor_->desktop_configuration();
+ if (update_screen_configuration_ || !desktop_config_.Equals(new_config)) {
+ update_screen_configuration_ = false;
+ desktop_config_ = new_config;
+ // If the display configuraiton has changed then refresh capturer data
+ // structures. Occasionally, the refresh and move handlers are lost when
+ // the screen mode changes, so re-register them here.
+ UnregisterRefreshAndMoveHandlers();
+ if (!RegisterRefreshAndMoveHandlers()) {
+ RTC_LOG(LS_ERROR) << "Failed to register refresh and move handlers.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+ ScreenConfigurationChanged();
+ }
+
+ // When screen is zoomed in/out, OSX only updates the part of Rects currently
+ // displayed on screen, with relative location to current top-left on screen.
+ // This will cause problems when we copy the dirty regions to the captured
+ // image. So we invalidate the whole screen to copy all the screen contents.
+ // With CGI method, the zooming will be ignored and the whole screen contents
+ // will be captured as before.
+ // With IOSurface method, the zoomed screen contents will be captured.
+ if (UAZoomEnabled()) {
+ helper_.InvalidateScreen(screen_pixel_bounds_.size());
+ }
+
+ DesktopRegion region;
+ helper_.TakeInvalidRegion(&region);
+
+ // If the current buffer is from an older generation then allocate a new one.
+ // Note that we can't reallocate other buffers at this point, since the caller
+ // may still be reading from them.
+ if (!queue_.current_frame()) queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(CreateFrame()));
+
+ DesktopFrame* current_frame = queue_.current_frame();
+
+ if (!CgBlit(*current_frame, region)) {
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+ std::unique_ptr<DesktopFrame> new_frame = queue_.current_frame()->Share();
+ if (detect_updated_region_) {
+ *new_frame->mutable_updated_region() = region;
+ } else {
+ new_frame->mutable_updated_region()->AddRect(DesktopRect::MakeSize(new_frame->size()));
+ }
+
+ if (current_display_) {
+ const MacDisplayConfiguration* config =
+ desktop_config_.FindDisplayConfigurationById(current_display_);
+ if (config) {
+ new_frame->set_top_left(
+ config->bounds.top_left().subtract(desktop_config_.bounds.top_left()));
+ }
+ }
+
+ helper_.set_size_most_recent(new_frame->size());
+
+ new_frame->set_capture_time_ms((rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec);
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(new_frame));
+}
+
+void ScreenCapturerMac::SetExcludedWindow(WindowId window) {
+ excluded_window_ = window;
+}
+
+bool ScreenCapturerMac::GetSourceList(SourceList* screens) {
+ RTC_DCHECK(screens->size() == 0);
+
+ for (MacDisplayConfigurations::iterator it = desktop_config_.displays.begin();
+ it != desktop_config_.displays.end();
+ ++it) {
+ Source value = {it->id, 0, std::string()};
+ screens->push_back(value);
+ }
+ return true;
+}
+
+bool ScreenCapturerMac::SelectSource(SourceId id) {
+ if (id == kFullDesktopScreenId) {
+ current_display_ = 0;
+ } else {
+ const MacDisplayConfiguration* config =
+ desktop_config_.FindDisplayConfigurationById(static_cast<CGDirectDisplayID>(id));
+ if (!config) return false;
+ current_display_ = config->id;
+ }
+
+ ScreenConfigurationChanged();
+ return true;
+}
+
+bool ScreenCapturerMac::CgBlit(const DesktopFrame& frame, const DesktopRegion& region) {
+ // If not all screen region is dirty, copy the entire contents of the previous capture buffer,
+ // to capture over.
+ if (queue_.previous_frame() && !region.Equals(DesktopRegion(screen_pixel_bounds_))) {
+ memcpy(frame.data(), queue_.previous_frame()->data(), frame.stride() * frame.size().height());
+ }
+
+ MacDisplayConfigurations displays_to_capture;
+ if (current_display_) {
+ // Capturing a single screen. Note that the screen id may change when
+ // screens are added or removed.
+ const MacDisplayConfiguration* config =
+ desktop_config_.FindDisplayConfigurationById(current_display_);
+ if (config) {
+ displays_to_capture.push_back(*config);
+ } else {
+ RTC_LOG(LS_ERROR) << "The selected screen cannot be found for capturing.";
+ return false;
+ }
+ } else {
+ // Capturing the whole desktop.
+ displays_to_capture = desktop_config_.displays;
+ }
+
+ // Create the window list once for all displays.
+ CFArrayRef window_list = CreateWindowListWithExclusion(excluded_window_);
+
+ for (size_t i = 0; i < displays_to_capture.size(); ++i) {
+ const MacDisplayConfiguration& display_config = displays_to_capture[i];
+
+ // Capturing mixed-DPI on one surface is hard, so we only return displays
+ // that match the "primary" display's DPI. The primary display is always
+ // the first in the list.
+ if (i > 0 && display_config.dip_to_pixel_scale != displays_to_capture[0].dip_to_pixel_scale) {
+ continue;
+ }
+ // Determine the display's position relative to the desktop, in pixels.
+ DesktopRect display_bounds = display_config.pixel_bounds;
+ display_bounds.Translate(-screen_pixel_bounds_.left(), -screen_pixel_bounds_.top());
+
+ // Determine which parts of the blit region, if any, lay within the monitor.
+ DesktopRegion copy_region = region;
+ copy_region.IntersectWith(display_bounds);
+ if (copy_region.is_empty()) continue;
+
+ // Translate the region to be copied into display-relative coordinates.
+ copy_region.Translate(-display_bounds.left(), -display_bounds.top());
+
+ DesktopRect excluded_window_bounds;
+ rtc::ScopedCFTypeRef<CGImageRef> excluded_image;
+ if (excluded_window_ && window_list) {
+ // Get the region of the excluded window relative the primary display.
+ excluded_window_bounds =
+ GetExcludedWindowPixelBounds(excluded_window_, display_config.dip_to_pixel_scale);
+ excluded_window_bounds.IntersectWith(display_config.pixel_bounds);
+
+ // Create the image under the excluded window first, because it's faster
+ // than captuing the whole display.
+ if (!excluded_window_bounds.is_empty()) {
+ excluded_image = CreateExcludedWindowRegionImage(
+ excluded_window_bounds, display_config.dip_to_pixel_scale, window_list);
+ }
+ }
+
+ std::unique_ptr<DesktopFrame> frame_source =
+ desktop_frame_provider_.TakeLatestFrameForDisplay(display_config.id);
+ if (!frame_source) {
+ continue;
+ }
+
+ const uint8_t* display_base_address = frame_source->data();
+ int src_bytes_per_row = frame_source->stride();
+ RTC_DCHECK(display_base_address);
+
+ // `frame_source` size may be different from display_bounds in case the screen was
+ // resized recently.
+ copy_region.IntersectWith(frame_source->rect());
+
+ // Copy the dirty region from the display buffer into our desktop buffer.
+ uint8_t* out_ptr = frame.GetFrameDataAtPos(display_bounds.top_left());
+ for (DesktopRegion::Iterator it(copy_region); !it.IsAtEnd(); it.Advance()) {
+ CopyRect(display_base_address,
+ src_bytes_per_row,
+ out_ptr,
+ frame.stride(),
+ DesktopFrame::kBytesPerPixel,
+ it.rect());
+ }
+
+ if (excluded_image) {
+ CGDataProviderRef provider = CGImageGetDataProvider(excluded_image.get());
+ rtc::ScopedCFTypeRef<CFDataRef> excluded_image_data(CGDataProviderCopyData(provider));
+ RTC_DCHECK(excluded_image_data);
+ display_base_address = CFDataGetBytePtr(excluded_image_data.get());
+ src_bytes_per_row = CGImageGetBytesPerRow(excluded_image.get());
+
+ // Translate the bounds relative to the desktop, because `frame` data
+ // starts from the desktop top-left corner.
+ DesktopRect window_bounds_relative_to_desktop(excluded_window_bounds);
+ window_bounds_relative_to_desktop.Translate(-screen_pixel_bounds_.left(),
+ -screen_pixel_bounds_.top());
+
+ DesktopRect rect_to_copy = DesktopRect::MakeSize(excluded_window_bounds.size());
+ rect_to_copy.IntersectWith(DesktopRect::MakeWH(CGImageGetWidth(excluded_image.get()),
+ CGImageGetHeight(excluded_image.get())));
+
+ if (CGImageGetBitsPerPixel(excluded_image.get()) / 8 == DesktopFrame::kBytesPerPixel) {
+ CopyRect(display_base_address,
+ src_bytes_per_row,
+ frame.GetFrameDataAtPos(window_bounds_relative_to_desktop.top_left()),
+ frame.stride(),
+ DesktopFrame::kBytesPerPixel,
+ rect_to_copy);
+ }
+ }
+ }
+ if (window_list) CFRelease(window_list);
+ return true;
+}
+
+void ScreenCapturerMac::ScreenConfigurationChanged() {
+ if (current_display_) {
+ const MacDisplayConfiguration* config =
+ desktop_config_.FindDisplayConfigurationById(current_display_);
+ screen_pixel_bounds_ = config ? config->pixel_bounds : DesktopRect();
+ dip_to_pixel_scale_ = config ? config->dip_to_pixel_scale : 1.0f;
+ } else {
+ screen_pixel_bounds_ = desktop_config_.pixel_bounds;
+ dip_to_pixel_scale_ = desktop_config_.dip_to_pixel_scale;
+ }
+
+ // Release existing buffers, which will be of the wrong size.
+ ReleaseBuffers();
+
+ // Clear the dirty region, in case the display is down-sizing.
+ helper_.ClearInvalidRegion();
+
+ // Re-mark the entire desktop as dirty.
+ helper_.InvalidateScreen(screen_pixel_bounds_.size());
+
+ // Make sure the frame buffers will be reallocated.
+ queue_.Reset();
+}
+
+bool ScreenCapturerMac::RegisterRefreshAndMoveHandlers() {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+ desktop_config_ = desktop_config_monitor_->desktop_configuration();
+ for (const auto& config : desktop_config_.displays) {
+ size_t pixel_width = config.pixel_bounds.width();
+ size_t pixel_height = config.pixel_bounds.height();
+ if (pixel_width == 0 || pixel_height == 0) continue;
+ CGDirectDisplayID display_id = config.id;
+ DesktopVector display_origin = config.pixel_bounds.top_left();
+
+ CGDisplayStreamFrameAvailableHandler handler = ^(CGDisplayStreamFrameStatus status,
+ uint64_t display_time,
+ IOSurfaceRef frame_surface,
+ CGDisplayStreamUpdateRef updateRef) {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+ if (status == kCGDisplayStreamFrameStatusStopped) return;
+
+ // Only pay attention to frame updates.
+ if (status != kCGDisplayStreamFrameStatusFrameComplete) return;
+
+ size_t count = 0;
+ const CGRect* rects =
+ CGDisplayStreamUpdateGetRects(updateRef, kCGDisplayStreamUpdateDirtyRects, &count);
+ if (count != 0) {
+ // According to CGDisplayStream.h, it's safe to call
+ // CGDisplayStreamStop() from within the callback.
+ ScreenRefresh(display_id, count, rects, display_origin, frame_surface);
+ }
+ };
+
+ rtc::ScopedCFTypeRef<CFDictionaryRef> properties_dict(
+ CFDictionaryCreate(kCFAllocatorDefault,
+ (const void* []){kCGDisplayStreamShowCursor},
+ (const void* []){kCFBooleanFalse},
+ 1,
+ &kCFTypeDictionaryKeyCallBacks,
+ &kCFTypeDictionaryValueCallBacks));
+
+ CGDisplayStreamRef display_stream = CGDisplayStreamCreate(
+ display_id, pixel_width, pixel_height, 'BGRA', properties_dict.get(), handler);
+
+ if (display_stream) {
+ CGError error = CGDisplayStreamStart(display_stream);
+ if (error != kCGErrorSuccess) return false;
+
+ CFRunLoopSourceRef source = CGDisplayStreamGetRunLoopSource(display_stream);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
+ display_streams_.push_back(display_stream);
+ }
+ }
+
+ return true;
+}
+
+void ScreenCapturerMac::UnregisterRefreshAndMoveHandlers() {
+ RTC_DCHECK(thread_checker_.IsCurrent());
+
+ for (CGDisplayStreamRef stream : display_streams_) {
+ CFRunLoopSourceRef source = CGDisplayStreamGetRunLoopSource(stream);
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
+ CGDisplayStreamStop(stream);
+ CFRelease(stream);
+ }
+ display_streams_.clear();
+
+ // Release obsolete io surfaces.
+ desktop_frame_provider_.Release();
+}
+
+void ScreenCapturerMac::ScreenRefresh(CGDirectDisplayID display_id,
+ CGRectCount count,
+ const CGRect* rect_array,
+ DesktopVector display_origin,
+ IOSurfaceRef io_surface) {
+ if (screen_pixel_bounds_.is_empty()) ScreenConfigurationChanged();
+
+ // The refresh rects are in display coordinates. We want to translate to
+ // framebuffer coordinates. If a specific display is being captured, then no
+ // change is necessary. If all displays are being captured, then we want to
+ // translate by the origin of the display.
+ DesktopVector translate_vector;
+ if (!current_display_) translate_vector = display_origin;
+
+ DesktopRegion region;
+ for (CGRectCount i = 0; i < count; ++i) {
+ // All rects are already in physical pixel coordinates.
+ DesktopRect rect = DesktopRect::MakeXYWH(rect_array[i].origin.x,
+ rect_array[i].origin.y,
+ rect_array[i].size.width,
+ rect_array[i].size.height);
+
+ rect.Translate(translate_vector);
+
+ region.AddRect(rect);
+ }
+ // Always having the latest iosurface before invalidating a region.
+ // See https://bugs.chromium.org/p/webrtc/issues/detail?id=8652 for details.
+ desktop_frame_provider_.InvalidateIOSurface(
+ display_id, rtc::ScopedCFTypeRef<IOSurfaceRef>(io_surface, rtc::RetainPolicy::RETAIN));
+ helper_.InvalidateRegion(region);
+}
+
+std::unique_ptr<DesktopFrame> ScreenCapturerMac::CreateFrame() {
+ std::unique_ptr<DesktopFrame> frame(new BasicDesktopFrame(screen_pixel_bounds_.size()));
+ frame->set_dpi(
+ DesktopVector(kStandardDPI * dip_to_pixel_scale_, kStandardDPI * dip_to_pixel_scale_));
+ return frame;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.cc b/third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.cc
new file mode 100644
index 0000000000..989ec7ea54
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.cc
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/mac/window_list_utils.h"
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include <algorithm>
+#include <cmath>
+#include <iterator>
+#include <limits>
+#include <list>
+#include <map>
+#include <memory>
+#include <utility>
+
+#include "rtc_base/checks.h"
+
+static_assert(static_cast<webrtc::WindowId>(kCGNullWindowID) ==
+ webrtc::kNullWindowId,
+ "kNullWindowId needs to equal to kCGNullWindowID.");
+
+namespace webrtc {
+
+namespace {
+
+// WindowName of the status indicator dot shown since Monterey in the taskbar.
+// Testing on 12.2.1 shows this is independent of system language setting.
+const CFStringRef kStatusIndicator = CFSTR("StatusIndicator");
+const CFStringRef kStatusIndicatorOwnerName = CFSTR("Window Server");
+
+bool ToUtf8(const CFStringRef str16, std::string* str8) {
+ size_t maxlen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str16),
+ kCFStringEncodingUTF8) +
+ 1;
+ std::unique_ptr<char[]> buffer(new char[maxlen]);
+ if (!buffer ||
+ !CFStringGetCString(str16, buffer.get(), maxlen, kCFStringEncodingUTF8)) {
+ return false;
+ }
+ str8->assign(buffer.get());
+ return true;
+}
+
+// Get CFDictionaryRef from `id` and call `on_window` against it. This function
+// returns false if native APIs fail, typically it indicates that the `id` does
+// not represent a window. `on_window` will not be called if false is returned
+// from this function.
+bool GetWindowRef(CGWindowID id,
+ rtc::FunctionView<void(CFDictionaryRef)> on_window) {
+ RTC_DCHECK(on_window);
+
+ // TODO(zijiehe): `id` is a 32-bit integer, casting it to an array seems not
+ // safe enough. Maybe we should create a new
+ // const void* arr[] = {
+ // reinterpret_cast<void*>(id) }
+ // };
+ CFArrayRef window_id_array =
+ CFArrayCreate(NULL, reinterpret_cast<const void**>(&id), 1, NULL);
+ CFArrayRef window_array =
+ CGWindowListCreateDescriptionFromArray(window_id_array);
+
+ bool result = false;
+ // TODO(zijiehe): CFArrayGetCount(window_array) should always return 1.
+ // Otherwise, we should treat it as failure.
+ if (window_array && CFArrayGetCount(window_array)) {
+ on_window(reinterpret_cast<CFDictionaryRef>(
+ CFArrayGetValueAtIndex(window_array, 0)));
+ result = true;
+ }
+
+ if (window_array) {
+ CFRelease(window_array);
+ }
+ CFRelease(window_id_array);
+ return result;
+}
+
+} // namespace
+
+bool GetWindowList(rtc::FunctionView<bool(CFDictionaryRef)> on_window,
+ bool ignore_minimized,
+ bool only_zero_layer) {
+ RTC_DCHECK(on_window);
+
+ // Only get on screen, non-desktop windows.
+ // According to
+ // https://developer.apple.com/documentation/coregraphics/cgwindowlistoption/1454105-optiononscreenonly
+ // , when kCGWindowListOptionOnScreenOnly is used, the order of windows are in
+ // decreasing z-order.
+ CFArrayRef window_array = CGWindowListCopyWindowInfo(
+ kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements,
+ kCGNullWindowID);
+ if (!window_array)
+ return false;
+
+ MacDesktopConfiguration desktop_config = MacDesktopConfiguration::GetCurrent(
+ MacDesktopConfiguration::TopLeftOrigin);
+
+ // Check windows to make sure they have an id, title, and use window layer
+ // other than 0.
+ CFIndex count = CFArrayGetCount(window_array);
+ for (CFIndex i = 0; i < count; i++) {
+ CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
+ CFArrayGetValueAtIndex(window_array, i));
+ if (!window) {
+ continue;
+ }
+
+ CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
+ CFDictionaryGetValue(window, kCGWindowNumber));
+ if (!window_id) {
+ continue;
+ }
+
+ CFNumberRef window_layer = reinterpret_cast<CFNumberRef>(
+ CFDictionaryGetValue(window, kCGWindowLayer));
+ if (!window_layer) {
+ continue;
+ }
+
+ // Skip windows with layer!=0 (menu, dock).
+ int layer;
+ if (!CFNumberGetValue(window_layer, kCFNumberIntType, &layer)) {
+ continue;
+ }
+ if (only_zero_layer && layer != 0) {
+ continue;
+ }
+
+ // Skip windows that are minimized and not full screen.
+ if (ignore_minimized && !IsWindowOnScreen(window) &&
+ !IsWindowFullScreen(desktop_config, window)) {
+ continue;
+ }
+
+ // If window title is empty, only consider it if it is either on screen or
+ // fullscreen.
+ CFStringRef window_title = reinterpret_cast<CFStringRef>(
+ CFDictionaryGetValue(window, kCGWindowName));
+ if (!window_title && !IsWindowOnScreen(window) &&
+ !IsWindowFullScreen(desktop_config, window)) {
+ continue;
+ }
+
+ CFStringRef window_owner_name = reinterpret_cast<CFStringRef>(
+ CFDictionaryGetValue(window, kCGWindowOwnerName));
+ // Ignore the red dot status indicator shown in the stats bar. Unlike the
+ // rest of the system UI it has a window_layer of 0, so was otherwise
+ // included. See crbug.com/1297731.
+ if (window_title && CFEqual(window_title, kStatusIndicator) &&
+ window_owner_name &&
+ CFEqual(window_owner_name, kStatusIndicatorOwnerName)) {
+ continue;
+ }
+
+ if (!on_window(window)) {
+ break;
+ }
+ }
+
+ CFRelease(window_array);
+ return true;
+}
+
+bool GetWindowList(DesktopCapturer::SourceList* windows,
+ bool ignore_minimized,
+ bool only_zero_layer) {
+ // Use a std::list so that iterators are preversed upon insertion and
+ // deletion.
+ std::list<DesktopCapturer::Source> sources;
+ std::map<int, std::list<DesktopCapturer::Source>::const_iterator> pid_itr_map;
+ const bool ret = GetWindowList(
+ [&sources, &pid_itr_map](CFDictionaryRef window) {
+ WindowId window_id = GetWindowId(window);
+ if (window_id != kNullWindowId) {
+ const std::string title = GetWindowTitle(window);
+ const int pid = GetWindowOwnerPid(window);
+ // Check if window for the same pid have been already inserted.
+ std::map<int,
+ std::list<DesktopCapturer::Source>::const_iterator>::iterator
+ itr = pid_itr_map.find(pid);
+
+ // Only consider empty titles if the app has no other window with a
+ // proper title.
+ if (title.empty()) {
+ std::string owner_name = GetWindowOwnerName(window);
+
+ // At this time we do not know if there will be other windows
+ // for the same pid unless they have been already inserted, hence
+ // the check in the map. Also skip the window if owner name is
+ // empty too.
+ if (!owner_name.empty() && (itr == pid_itr_map.end())) {
+ sources.push_back(DesktopCapturer::Source{window_id, pid, owner_name});
+ RTC_DCHECK(!sources.empty());
+ // Get an iterator on the last valid element in the source list.
+ std::list<DesktopCapturer::Source>::const_iterator last_source =
+ --sources.end();
+ pid_itr_map.insert(
+ std::pair<int,
+ std::list<DesktopCapturer::Source>::const_iterator>(
+ pid, last_source));
+ }
+ } else {
+ sources.push_back(DesktopCapturer::Source{window_id, pid, title});
+ // Once the window with empty title has been removed no other empty
+ // windows are allowed for the same pid.
+ if (itr != pid_itr_map.end() && (itr->second != sources.end())) {
+ sources.erase(itr->second);
+ // sdt::list::end() never changes during the lifetime of that
+ // list.
+ itr->second = sources.end();
+ }
+ }
+ }
+ return true;
+ },
+ ignore_minimized, only_zero_layer);
+
+ if (!ret)
+ return false;
+
+ RTC_DCHECK(windows);
+ windows->reserve(windows->size() + sources.size());
+ std::copy(std::begin(sources), std::end(sources),
+ std::back_inserter(*windows));
+
+ return true;
+}
+
+// Returns true if the window is occupying a full screen.
+bool IsWindowFullScreen(const MacDesktopConfiguration& desktop_config,
+ CFDictionaryRef window) {
+ bool fullscreen = false;
+ CFDictionaryRef bounds_ref = reinterpret_cast<CFDictionaryRef>(
+ CFDictionaryGetValue(window, kCGWindowBounds));
+
+ CGRect bounds;
+ if (bounds_ref &&
+ CGRectMakeWithDictionaryRepresentation(bounds_ref, &bounds)) {
+ for (MacDisplayConfigurations::const_iterator it =
+ desktop_config.displays.begin();
+ it != desktop_config.displays.end(); it++) {
+ if (it->bounds.equals(
+ DesktopRect::MakeXYWH(bounds.origin.x, bounds.origin.y,
+ bounds.size.width, bounds.size.height))) {
+ fullscreen = true;
+ break;
+ }
+ }
+ }
+
+ return fullscreen;
+}
+
+bool IsWindowFullScreen(const MacDesktopConfiguration& desktop_config,
+ CGWindowID id) {
+ bool fullscreen = false;
+ GetWindowRef(id, [&](CFDictionaryRef window) {
+ fullscreen = IsWindowFullScreen(desktop_config, window);
+ });
+ return fullscreen;
+}
+
+bool IsWindowOnScreen(CFDictionaryRef window) {
+ CFBooleanRef on_screen = reinterpret_cast<CFBooleanRef>(
+ CFDictionaryGetValue(window, kCGWindowIsOnscreen));
+ return on_screen != NULL && CFBooleanGetValue(on_screen);
+}
+
+bool IsWindowOnScreen(CGWindowID id) {
+ bool on_screen;
+ if (GetWindowRef(id, [&on_screen](CFDictionaryRef window) {
+ on_screen = IsWindowOnScreen(window);
+ })) {
+ return on_screen;
+ }
+ return false;
+}
+
+std::string GetWindowTitle(CFDictionaryRef window) {
+ CFStringRef title = reinterpret_cast<CFStringRef>(
+ CFDictionaryGetValue(window, kCGWindowName));
+ std::string result;
+ if (title && ToUtf8(title, &result)) {
+ return result;
+ }
+
+ return std::string();
+}
+
+std::string GetWindowTitle(CGWindowID id) {
+ std::string title;
+ if (GetWindowRef(id, [&title](CFDictionaryRef window) {
+ title = GetWindowTitle(window);
+ })) {
+ return title;
+ }
+ return std::string();
+}
+
+std::string GetWindowOwnerName(CFDictionaryRef window) {
+ CFStringRef owner_name = reinterpret_cast<CFStringRef>(
+ CFDictionaryGetValue(window, kCGWindowOwnerName));
+ std::string result;
+ if (owner_name && ToUtf8(owner_name, &result)) {
+ return result;
+ }
+ return std::string();
+}
+
+std::string GetWindowOwnerName(CGWindowID id) {
+ std::string owner_name;
+ if (GetWindowRef(id, [&owner_name](CFDictionaryRef window) {
+ owner_name = GetWindowOwnerName(window);
+ })) {
+ return owner_name;
+ }
+ return std::string();
+}
+
+WindowId GetWindowId(CFDictionaryRef window) {
+ CFNumberRef window_id = reinterpret_cast<CFNumberRef>(
+ CFDictionaryGetValue(window, kCGWindowNumber));
+ if (!window_id) {
+ return kNullWindowId;
+ }
+
+ // Note: WindowId is 64-bit on 64-bit system, but CGWindowID is always 32-bit.
+ // CFNumberGetValue() fills only top 32 bits, so we should use CGWindowID to
+ // receive the window id.
+ CGWindowID id;
+ if (!CFNumberGetValue(window_id, kCFNumberIntType, &id)) {
+ return kNullWindowId;
+ }
+
+ return id;
+}
+
+int GetWindowOwnerPid(CFDictionaryRef window) {
+ CFNumberRef window_pid = reinterpret_cast<CFNumberRef>(
+ CFDictionaryGetValue(window, kCGWindowOwnerPID));
+ if (!window_pid) {
+ return 0;
+ }
+
+ int pid;
+ if (!CFNumberGetValue(window_pid, kCFNumberIntType, &pid)) {
+ return 0;
+ }
+
+ return pid;
+}
+
+int GetWindowOwnerPid(CGWindowID id) {
+ int pid;
+ if (GetWindowRef(id, [&pid](CFDictionaryRef window) {
+ pid = GetWindowOwnerPid(window);
+ })) {
+ return pid;
+ }
+ return 0;
+}
+
+float GetScaleFactorAtPosition(const MacDesktopConfiguration& desktop_config,
+ DesktopVector position) {
+ // Find the dpi to physical pixel scale for the screen where the mouse cursor
+ // is.
+ for (auto it = desktop_config.displays.begin();
+ it != desktop_config.displays.end(); ++it) {
+ if (it->bounds.Contains(position)) {
+ return it->dip_to_pixel_scale;
+ }
+ }
+ return 1;
+}
+
+float GetWindowScaleFactor(CGWindowID id, DesktopSize size) {
+ DesktopRect window_bounds = GetWindowBounds(id);
+ float scale = 1.0f;
+
+ if (!window_bounds.is_empty() && !size.is_empty()) {
+ float scale_x = size.width() / window_bounds.width();
+ float scale_y = size.height() / window_bounds.height();
+ // Currently the scale in X and Y directions must be same.
+ if ((std::fabs(scale_x - scale_y) <=
+ std::numeric_limits<float>::epsilon() * std::max(scale_x, scale_y)) &&
+ scale_x > 0.0f) {
+ scale = scale_x;
+ }
+ }
+
+ return scale;
+}
+
+DesktopRect GetWindowBounds(CFDictionaryRef window) {
+ CFDictionaryRef window_bounds = reinterpret_cast<CFDictionaryRef>(
+ CFDictionaryGetValue(window, kCGWindowBounds));
+ if (!window_bounds) {
+ return DesktopRect();
+ }
+
+ CGRect gc_window_rect;
+ if (!CGRectMakeWithDictionaryRepresentation(window_bounds, &gc_window_rect)) {
+ return DesktopRect();
+ }
+
+ return DesktopRect::MakeXYWH(gc_window_rect.origin.x, gc_window_rect.origin.y,
+ gc_window_rect.size.width,
+ gc_window_rect.size.height);
+}
+
+DesktopRect GetWindowBounds(CGWindowID id) {
+ DesktopRect result;
+ if (GetWindowRef(id, [&result](CFDictionaryRef window) {
+ result = GetWindowBounds(window);
+ })) {
+ return result;
+ }
+ return DesktopRect();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.h b/third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.h
new file mode 100644
index 0000000000..a9b1e7007c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mac/window_list_utils.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MAC_WINDOW_LIST_UTILS_H_
+#define MODULES_DESKTOP_CAPTURE_MAC_WINDOW_LIST_UTILS_H_
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include <string>
+#include "api/function_view.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/mac/desktop_configuration.h"
+
+namespace webrtc {
+
+// Iterates all on-screen windows in decreasing z-order and sends them
+// one-by-one to `on_window` function. If `on_window` returns false, this
+// function returns immediately. GetWindowList() returns false if native APIs
+// failed. Menus, dock (if `only_zero_layer`), minimized windows (if
+// `ignore_minimized` is true) and any windows which do not have a valid window
+// id or title will be ignored.
+bool GetWindowList(rtc::FunctionView<bool(CFDictionaryRef)> on_window,
+ bool ignore_minimized,
+ bool only_zero_layer);
+
+// Another helper function to get the on-screen windows.
+bool GetWindowList(DesktopCapturer::SourceList* windows,
+ bool ignore_minimized,
+ bool only_zero_layer);
+
+// Returns true if the window is occupying a full screen.
+bool IsWindowFullScreen(const MacDesktopConfiguration& desktop_config,
+ CFDictionaryRef window);
+
+// Returns true if the window is occupying a full screen.
+bool IsWindowFullScreen(const MacDesktopConfiguration& desktop_config,
+ CGWindowID id);
+
+// Returns true if the `window` is on screen. This function returns false if
+// native APIs fail.
+bool IsWindowOnScreen(CFDictionaryRef window);
+
+// Returns true if the window is on screen. This function returns false if
+// native APIs fail or `id` cannot be found.
+bool IsWindowOnScreen(CGWindowID id);
+
+// Returns utf-8 encoded title of `window`. If `window` is not a window or no
+// valid title can be retrieved, this function returns an empty string.
+std::string GetWindowTitle(CFDictionaryRef window);
+
+// Returns utf-8 encoded title of window `id`. If `id` cannot be found or no
+// valid title can be retrieved, this function returns an empty string.
+std::string GetWindowTitle(CGWindowID id);
+
+// Returns utf-8 encoded owner name of `window`. If `window` is not a window or
+// if no valid owner name can be retrieved, returns an empty string.
+std::string GetWindowOwnerName(CFDictionaryRef window);
+
+// Returns utf-8 encoded owner name of the given window `id`. If `id` cannot be
+// found or if no valid owner name can be retrieved, returns an empty string.
+std::string GetWindowOwnerName(CGWindowID id);
+
+// Returns id of `window`. If `window` is not a window or the window id cannot
+// be retrieved, this function returns kNullWindowId.
+WindowId GetWindowId(CFDictionaryRef window);
+
+// Returns the pid of the process owning `window`. Return 0 if `window` is not
+// a window or no valid owner can be retrieved.
+int GetWindowOwnerPid(CFDictionaryRef window);
+
+// Returns the pid of the process owning the window `id`. Return 0 if `id`
+// cannot be found or no valid owner can be retrieved.
+int GetWindowOwnerPid(CGWindowID id);
+
+// Returns the DIP to physical pixel scale at `position`. `position` is in
+// *unscaled* system coordinate, i.e. it's device-independent and the primary
+// monitor starts from (0, 0). If `position` is out of the system display, this
+// function returns 1.
+float GetScaleFactorAtPosition(const MacDesktopConfiguration& desktop_config,
+ DesktopVector position);
+
+// Returns the DIP to physical pixel scale factor of the window with `id`.
+// The bounds of the window with `id` is in DIP coordinates and `size` is the
+// CGImage size of the window with `id` in physical coordinates. Comparing them
+// can give the current scale factor.
+// If the window overlaps multiple monitors, OS will decide on which monitor the
+// window is displayed and use its scale factor to the window. So this method
+// still works.
+float GetWindowScaleFactor(CGWindowID id, DesktopSize size);
+
+// Returns the bounds of `window`. If `window` is not a window or the bounds
+// cannot be retrieved, this function returns an empty DesktopRect. The returned
+// DesktopRect is in system coordinate, i.e. the primary monitor always starts
+// from (0, 0).
+// Deprecated: This function should be avoided in favor of the overload with
+// MacDesktopConfiguration.
+DesktopRect GetWindowBounds(CFDictionaryRef window);
+
+// Returns the bounds of window with `id`. If `id` does not represent a window
+// or the bounds cannot be retrieved, this function returns an empty
+// DesktopRect. The returned DesktopRect is in system coordinates.
+// Deprecated: This function should be avoided in favor of the overload with
+// MacDesktopConfiguration.
+DesktopRect GetWindowBounds(CGWindowID id);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MAC_WINDOW_LIST_UTILS_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mock_desktop_capturer_callback.cc b/third_party/libwebrtc/modules/desktop_capture/mock_desktop_capturer_callback.cc
new file mode 100644
index 0000000000..de77d99e18
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mock_desktop_capturer_callback.cc
@@ -0,0 +1,23 @@
+/* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/mock_desktop_capturer_callback.h"
+
+namespace webrtc {
+
+MockDesktopCapturerCallback::MockDesktopCapturerCallback() = default;
+MockDesktopCapturerCallback::~MockDesktopCapturerCallback() = default;
+
+void MockDesktopCapturerCallback::OnCaptureResult(
+ DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame> frame) {
+ OnCaptureResultPtr(result, &frame);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mock_desktop_capturer_callback.h b/third_party/libwebrtc/modules/desktop_capture/mock_desktop_capturer_callback.h
new file mode 100644
index 0000000000..08de5ad737
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mock_desktop_capturer_callback.h
@@ -0,0 +1,39 @@
+/* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MOCK_DESKTOP_CAPTURER_CALLBACK_H_
+#define MODULES_DESKTOP_CAPTURE_MOCK_DESKTOP_CAPTURER_CALLBACK_H_
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+class MockDesktopCapturerCallback : public DesktopCapturer::Callback {
+ public:
+ MockDesktopCapturerCallback();
+ ~MockDesktopCapturerCallback() override;
+
+ MockDesktopCapturerCallback(const MockDesktopCapturerCallback&) = delete;
+ MockDesktopCapturerCallback& operator=(const MockDesktopCapturerCallback&) =
+ delete;
+
+ MOCK_METHOD(void,
+ OnCaptureResultPtr,
+ (DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame>* frame));
+ void OnCaptureResult(DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame> frame) final override;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MOCK_DESKTOP_CAPTURER_CALLBACK_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mouse_cursor.cc b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor.cc
new file mode 100644
index 0000000000..e826552b0f
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor.cc
@@ -0,0 +1,36 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/mouse_cursor.h"
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+MouseCursor::MouseCursor() {}
+
+MouseCursor::MouseCursor(DesktopFrame* image, const DesktopVector& hotspot)
+ : image_(image), hotspot_(hotspot) {
+ RTC_DCHECK(0 <= hotspot_.x() && hotspot_.x() <= image_->size().width());
+ RTC_DCHECK(0 <= hotspot_.y() && hotspot_.y() <= image_->size().height());
+}
+
+MouseCursor::~MouseCursor() {}
+
+// static
+MouseCursor* MouseCursor::CopyOf(const MouseCursor& cursor) {
+ return cursor.image()
+ ? new MouseCursor(BasicDesktopFrame::CopyOf(*cursor.image()),
+ cursor.hotspot())
+ : new MouseCursor();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mouse_cursor.h b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor.h
new file mode 100644
index 0000000000..2dd793179b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_H_
+#define MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_H_
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+class RTC_EXPORT MouseCursor {
+ public:
+ MouseCursor();
+
+ // Takes ownership of `image`. `hotspot` must be within `image` boundaries.
+ MouseCursor(DesktopFrame* image, const DesktopVector& hotspot);
+
+ ~MouseCursor();
+
+ MouseCursor(const MouseCursor&) = delete;
+ MouseCursor& operator=(const MouseCursor&) = delete;
+
+ static MouseCursor* CopyOf(const MouseCursor& cursor);
+
+ void set_image(DesktopFrame* image) { image_.reset(image); }
+ const DesktopFrame* image() const { return image_.get(); }
+
+ void set_hotspot(const DesktopVector& hotspot) { hotspot_ = hotspot; }
+ const DesktopVector& hotspot() const { return hotspot_; }
+
+ private:
+ std::unique_ptr<DesktopFrame> image_;
+ DesktopVector hotspot_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor.h b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor.h
new file mode 100644
index 0000000000..e88a5b7201
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_MONITOR_H_
+#define MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_MONITOR_H_
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+class DesktopCaptureOptions;
+class DesktopFrame;
+class MouseCursor;
+
+// Captures mouse shape and position.
+class MouseCursorMonitor {
+ public:
+ // Deprecated: CursorState will not be provided.
+ enum CursorState {
+ // Cursor on top of the window including window decorations.
+ INSIDE,
+
+ // Cursor is outside of the window.
+ OUTSIDE,
+ };
+
+ enum Mode {
+ // Capture only shape of the mouse cursor, but not position.
+ SHAPE_ONLY,
+
+ // Capture both, mouse cursor shape and position.
+ SHAPE_AND_POSITION,
+ };
+
+ // Callback interface used to pass current mouse cursor position and shape.
+ class Callback {
+ public:
+ // Called in response to Capture() when the cursor shape has changed. Must
+ // take ownership of `cursor`.
+ virtual void OnMouseCursor(MouseCursor* cursor) = 0;
+
+ // Called in response to Capture(). `position` indicates cursor position
+ // relative to the `window` specified in the constructor.
+ // Deprecated: use the following overload instead.
+ virtual void OnMouseCursorPosition(CursorState state,
+ const DesktopVector& position) {}
+
+ // Called in response to Capture(). `position` indicates cursor absolute
+ // position on the system in fullscreen coordinate, i.e. the top-left
+ // monitor always starts from (0, 0).
+ // The coordinates of the position is controlled by OS, but it's always
+ // consistent with DesktopFrame.rect().top_left().
+ // TODO(zijiehe): Ensure all implementations return the absolute position.
+ // TODO(zijiehe): Current this overload works correctly only when capturing
+ // mouse cursor against fullscreen.
+ virtual void OnMouseCursorPosition(const DesktopVector& position) {}
+
+ protected:
+ virtual ~Callback() {}
+ };
+
+ virtual ~MouseCursorMonitor() {}
+
+ // Creates a capturer that notifies of mouse cursor events while the cursor is
+ // over the specified window.
+ //
+ // Deprecated: use Create() function.
+ static MouseCursorMonitor* CreateForWindow(
+ const DesktopCaptureOptions& options,
+ WindowId window);
+
+ // Creates a capturer that monitors the mouse cursor shape and position over
+ // the specified screen.
+ //
+ // Deprecated: use Create() function.
+ static RTC_EXPORT MouseCursorMonitor* CreateForScreen(
+ const DesktopCaptureOptions& options,
+ ScreenId screen);
+
+ // Creates a capturer that monitors the mouse cursor shape and position across
+ // the entire desktop. The capturer ensures that the top-left monitor starts
+ // from (0, 0).
+ static std::unique_ptr<MouseCursorMonitor> Create(
+ const DesktopCaptureOptions& options);
+
+ // Initializes the monitor with the `callback`, which must remain valid until
+ // capturer is destroyed.
+ virtual void Init(Callback* callback, Mode mode) = 0;
+
+ // Captures current cursor shape and position (depending on the `mode` passed
+ // to Init()). Calls Callback::OnMouseCursor() if cursor shape has
+ // changed since the last call (or when Capture() is called for the first
+ // time) and then Callback::OnMouseCursorPosition() if mode is set to
+ // SHAPE_AND_POSITION.
+ virtual void Capture() = 0;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_MOUSE_CURSOR_MONITOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc
new file mode 100644
index 0000000000..6162970074
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/mouse_cursor_monitor.h"
+
+#if defined(WEBRTC_USE_X11)
+#include "modules/desktop_capture/linux/x11/mouse_cursor_monitor_x11.h"
+#endif // defined(WEBRTC_USE_X11)
+
+#if defined(WEBRTC_USE_PIPEWIRE) && !defined(WEBRTC_MOZILLA_BUILD)
+#include "modules/desktop_capture/linux/wayland/mouse_cursor_monitor_pipewire.h"
+#endif // defined(WEBRTC_USE_PIPEWIRE)
+
+namespace webrtc {
+
+// static
+MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
+ const DesktopCaptureOptions& options,
+ WindowId window) {
+#if defined(WEBRTC_USE_X11)
+ return MouseCursorMonitorX11::CreateForWindow(options, window);
+#else
+ return nullptr;
+#endif // defined(WEBRTC_USE_X11)
+}
+
+// static
+MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
+ const DesktopCaptureOptions& options,
+ ScreenId screen) {
+#if defined(WEBRTC_USE_X11)
+ return MouseCursorMonitorX11::CreateForScreen(options, screen);
+#else
+ return nullptr;
+#endif // defined(WEBRTC_USE_X11)
+}
+
+// static
+std::unique_ptr<MouseCursorMonitor> MouseCursorMonitor::Create(
+ const DesktopCaptureOptions& options) {
+#if defined(WEBRTC_USE_PIPEWIRE) && !defined(WEBRTC_MOZILLA_BUILD)
+ if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland() &&
+ options.screencast_stream()) {
+ return std::make_unique<MouseCursorMonitorPipeWire>(options);
+ }
+#endif // defined(WEBRTC_USE_PIPEWIRE)
+
+#if defined(WEBRTC_USE_X11)
+ return MouseCursorMonitorX11::Create(options);
+#else
+ return nullptr;
+#endif // defined(WEBRTC_USE_X11)
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm
new file mode 100644
index 0000000000..512103ab5e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_mac.mm
@@ -0,0 +1,213 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/mouse_cursor_monitor.h"
+
+
+#include <memory>
+
+#include <ApplicationServices/ApplicationServices.h>
+#include <Cocoa/Cocoa.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/mac/desktop_configuration.h"
+#include "modules/desktop_capture/mac/desktop_configuration_monitor.h"
+#include "modules/desktop_capture/mac/window_list_utils.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+namespace {
+CGImageRef CreateScaledCGImage(CGImageRef image, int width, int height) {
+ // Create context, keeping original image properties.
+ CGColorSpaceRef colorspace = CGImageGetColorSpace(image);
+ CGContextRef context = CGBitmapContextCreate(nullptr,
+ width,
+ height,
+ CGImageGetBitsPerComponent(image),
+ width * DesktopFrame::kBytesPerPixel,
+ colorspace,
+ CGImageGetBitmapInfo(image));
+
+ if (!context) return nil;
+
+ // Draw image to context, resizing it.
+ CGContextDrawImage(context, CGRectMake(0, 0, width, height), image);
+ // Extract resulting image from context.
+ CGImageRef imgRef = CGBitmapContextCreateImage(context);
+ CGContextRelease(context);
+
+ return imgRef;
+}
+} // namespace
+
+class MouseCursorMonitorMac : public MouseCursorMonitor {
+ public:
+ MouseCursorMonitorMac(const DesktopCaptureOptions& options,
+ CGWindowID window_id,
+ ScreenId screen_id);
+ ~MouseCursorMonitorMac() override;
+
+ void Init(Callback* callback, Mode mode) override;
+ void Capture() override;
+
+ private:
+ static void DisplaysReconfiguredCallback(CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags,
+ void *user_parameter);
+ void DisplaysReconfigured(CGDirectDisplayID display,
+ CGDisplayChangeSummaryFlags flags);
+
+ void CaptureImage(float scale);
+
+ rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
+ CGWindowID window_id_;
+ ScreenId screen_id_;
+ Callback* callback_ = NULL;
+ Mode mode_;
+ __strong NSImage* last_cursor_ = NULL;
+};
+
+MouseCursorMonitorMac::MouseCursorMonitorMac(const DesktopCaptureOptions& options,
+ CGWindowID window_id,
+ ScreenId screen_id)
+ : configuration_monitor_(options.configuration_monitor()),
+ window_id_(window_id),
+ screen_id_(screen_id),
+ mode_(SHAPE_AND_POSITION) {
+ RTC_DCHECK(window_id == kCGNullWindowID || screen_id == kInvalidScreenId);
+}
+
+MouseCursorMonitorMac::~MouseCursorMonitorMac() {}
+
+void MouseCursorMonitorMac::Init(Callback* callback, Mode mode) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+
+ callback_ = callback;
+ mode_ = mode;
+}
+
+void MouseCursorMonitorMac::Capture() {
+ RTC_DCHECK(callback_);
+
+ CGEventRef event = CGEventCreate(NULL);
+ CGPoint gc_position = CGEventGetLocation(event);
+ CFRelease(event);
+
+ DesktopVector position(gc_position.x, gc_position.y);
+
+ MacDesktopConfiguration configuration =
+ configuration_monitor_->desktop_configuration();
+ float scale = GetScaleFactorAtPosition(configuration, position);
+
+ CaptureImage(scale);
+
+ if (mode_ != SHAPE_AND_POSITION)
+ return;
+
+ // Always report cursor position in DIP pixel.
+ callback_->OnMouseCursorPosition(
+ position.subtract(configuration.bounds.top_left()));
+}
+
+void MouseCursorMonitorMac::CaptureImage(float scale) {
+ NSCursor* nscursor = [NSCursor currentSystemCursor];
+
+ NSImage* nsimage = [nscursor image];
+ if (nsimage == nil || !nsimage.isValid) {
+ return;
+ }
+ NSSize nssize = [nsimage size]; // DIP size
+
+ // No need to caputre cursor image if it's unchanged since last capture.
+ if (last_cursor_ && [[nsimage TIFFRepresentation] isEqual:[last_cursor_ TIFFRepresentation]]) return;
+ last_cursor_ = nsimage;
+
+ DesktopSize size(round(nssize.width * scale),
+ round(nssize.height * scale)); // Pixel size
+ NSPoint nshotspot = [nscursor hotSpot];
+ DesktopVector hotspot(
+ std::max(0,
+ std::min(size.width(), static_cast<int>(nshotspot.x * scale))),
+ std::max(0,
+ std::min(size.height(), static_cast<int>(nshotspot.y * scale))));
+ CGImageRef cg_image =
+ [nsimage CGImageForProposedRect:NULL context:nil hints:nil];
+ if (!cg_image)
+ return;
+
+ // Before 10.12, OSX may report 1X cursor on Retina screen. (See
+ // crbug.com/632995.) After 10.12, OSX may report 2X cursor on non-Retina
+ // screen. (See crbug.com/671436.) So scaling the cursor if needed.
+ CGImageRef scaled_cg_image = nil;
+ if (CGImageGetWidth(cg_image) != static_cast<size_t>(size.width())) {
+ scaled_cg_image = CreateScaledCGImage(cg_image, size.width(), size.height());
+ if (scaled_cg_image != nil) {
+ cg_image = scaled_cg_image;
+ }
+ }
+ if (CGImageGetBitsPerPixel(cg_image) != DesktopFrame::kBytesPerPixel * 8 ||
+ CGImageGetWidth(cg_image) != static_cast<size_t>(size.width()) ||
+ CGImageGetBitsPerComponent(cg_image) != 8) {
+ if (scaled_cg_image != nil) CGImageRelease(scaled_cg_image);
+ return;
+ }
+
+ CGDataProviderRef provider = CGImageGetDataProvider(cg_image);
+ CFDataRef image_data_ref = CGDataProviderCopyData(provider);
+ if (image_data_ref == NULL) {
+ if (scaled_cg_image != nil) CGImageRelease(scaled_cg_image);
+ return;
+ }
+
+ const uint8_t* src_data =
+ reinterpret_cast<const uint8_t*>(CFDataGetBytePtr(image_data_ref));
+
+ // Create a MouseCursor that describes the cursor and pass it to
+ // the client.
+ std::unique_ptr<DesktopFrame> image(
+ new BasicDesktopFrame(DesktopSize(size.width(), size.height())));
+
+ int src_stride = CGImageGetBytesPerRow(cg_image);
+ image->CopyPixelsFrom(src_data, src_stride, DesktopRect::MakeSize(size));
+
+ CFRelease(image_data_ref);
+ if (scaled_cg_image != nil) CGImageRelease(scaled_cg_image);
+
+ std::unique_ptr<MouseCursor> cursor(
+ new MouseCursor(image.release(), hotspot));
+
+ callback_->OnMouseCursor(cursor.release());
+}
+
+MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
+ const DesktopCaptureOptions& options, WindowId window) {
+ return new MouseCursorMonitorMac(options, window, kInvalidScreenId);
+}
+
+MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
+ const DesktopCaptureOptions& options,
+ ScreenId screen) {
+ return new MouseCursorMonitorMac(options, kCGNullWindowID, screen);
+}
+
+std::unique_ptr<MouseCursorMonitor> MouseCursorMonitor::Create(
+ const DesktopCaptureOptions& options) {
+ return std::unique_ptr<MouseCursorMonitor>(
+ CreateForScreen(options, kFullDesktopScreenId));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_null.cc b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_null.cc
new file mode 100644
index 0000000000..ab1bc2fa33
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_null.cc
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/mouse_cursor_monitor.h"
+
+namespace webrtc {
+
+MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
+ const DesktopCaptureOptions& options,
+ WindowId window) {
+ return NULL;
+}
+
+MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
+ const DesktopCaptureOptions& options,
+ ScreenId screen) {
+ return NULL;
+}
+
+std::unique_ptr<MouseCursorMonitor> MouseCursorMonitor::Create(
+ const DesktopCaptureOptions& options) {
+ return std::unique_ptr<MouseCursorMonitor>(
+ CreateForScreen(options, kFullDesktopScreenId));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc
new file mode 100644
index 0000000000..f771276a2b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_unittest.cc
@@ -0,0 +1,128 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/mouse_cursor_monitor.h"
+
+#include <stddef.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "rtc_base/checks.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+class MouseCursorMonitorTest : public ::testing::Test,
+ public MouseCursorMonitor::Callback {
+ public:
+ MouseCursorMonitorTest() : position_received_(false) {}
+
+ // MouseCursorMonitor::Callback interface
+ void OnMouseCursor(MouseCursor* cursor_image) override {
+ cursor_image_.reset(cursor_image);
+ }
+
+ void OnMouseCursorPosition(const DesktopVector& position) override {
+ position_ = position;
+ position_received_ = true;
+ }
+
+ protected:
+ std::unique_ptr<MouseCursor> cursor_image_;
+ DesktopVector position_;
+ bool position_received_;
+};
+
+// TODO(sergeyu): On Mac we need to initialize NSApplication before running the
+// tests. Figure out how to do that without breaking other tests in
+// modules_unittests and enable these tests on Mac.
+// https://code.google.com/p/webrtc/issues/detail?id=2532
+//
+// Disabled on Windows due to flake, see:
+// https://code.google.com/p/webrtc/issues/detail?id=3408
+// Disabled on Linux due to flake, see:
+// https://code.google.com/p/webrtc/issues/detail?id=3245
+#if !defined(WEBRTC_MAC) && !defined(WEBRTC_WIN) && !defined(WEBRTC_LINUX)
+#define MAYBE(x) x
+#else
+#define MAYBE(x) DISABLED_##x
+#endif
+
+TEST_F(MouseCursorMonitorTest, MAYBE(FromScreen)) {
+ std::unique_ptr<MouseCursorMonitor> capturer(
+ MouseCursorMonitor::CreateForScreen(
+ DesktopCaptureOptions::CreateDefault(),
+ webrtc::kFullDesktopScreenId));
+ RTC_DCHECK(capturer.get());
+ capturer->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION);
+ capturer->Capture();
+
+ EXPECT_TRUE(cursor_image_.get());
+ EXPECT_GE(cursor_image_->hotspot().x(), 0);
+ EXPECT_LE(cursor_image_->hotspot().x(),
+ cursor_image_->image()->size().width());
+ EXPECT_GE(cursor_image_->hotspot().y(), 0);
+ EXPECT_LE(cursor_image_->hotspot().y(),
+ cursor_image_->image()->size().height());
+
+ EXPECT_TRUE(position_received_);
+}
+
+TEST_F(MouseCursorMonitorTest, MAYBE(FromWindow)) {
+ DesktopCaptureOptions options = DesktopCaptureOptions::CreateDefault();
+
+ // First get list of windows.
+ std::unique_ptr<DesktopCapturer> window_capturer(
+ DesktopCapturer::CreateWindowCapturer(options));
+
+ // If window capturing is not supported then skip this test.
+ if (!window_capturer.get())
+ return;
+
+ DesktopCapturer::SourceList sources;
+ EXPECT_TRUE(window_capturer->GetSourceList(&sources));
+
+ // Iterate over all windows and try capturing mouse cursor for each of them.
+ for (size_t i = 0; i < sources.size(); ++i) {
+ cursor_image_.reset();
+ position_received_ = false;
+
+ std::unique_ptr<MouseCursorMonitor> capturer(
+ MouseCursorMonitor::CreateForWindow(
+ DesktopCaptureOptions::CreateDefault(), sources[i].id));
+ RTC_DCHECK(capturer.get());
+
+ capturer->Init(this, MouseCursorMonitor::SHAPE_AND_POSITION);
+ capturer->Capture();
+
+ EXPECT_TRUE(cursor_image_.get());
+ EXPECT_TRUE(position_received_);
+ }
+}
+
+// Make sure that OnMouseCursorPosition() is not called in the SHAPE_ONLY mode.
+TEST_F(MouseCursorMonitorTest, MAYBE(ShapeOnly)) {
+ std::unique_ptr<MouseCursorMonitor> capturer(
+ MouseCursorMonitor::CreateForScreen(
+ DesktopCaptureOptions::CreateDefault(),
+ webrtc::kFullDesktopScreenId));
+ RTC_DCHECK(capturer.get());
+ capturer->Init(this, MouseCursorMonitor::SHAPE_ONLY);
+ capturer->Capture();
+
+ EXPECT_TRUE(cursor_image_.get());
+ EXPECT_FALSE(position_received_);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc
new file mode 100644
index 0000000000..c22425b5a2
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/mouse_cursor_monitor_win.cc
@@ -0,0 +1,222 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <string.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/mouse_cursor_monitor.h"
+#include "modules/desktop_capture/win/cursor.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "modules/desktop_capture/win/window_capture_utils.h"
+#include "rtc_base/logging.h"
+
+#include <windows.h>
+
+namespace webrtc {
+
+namespace {
+
+bool IsSameCursorShape(const CURSORINFO& left, const CURSORINFO& right) {
+ // If the cursors are not showing, we do not care the hCursor handle.
+ return left.flags == right.flags &&
+ (left.flags != CURSOR_SHOWING || left.hCursor == right.hCursor);
+}
+
+} // namespace
+
+class MouseCursorMonitorWin : public MouseCursorMonitor {
+ public:
+ explicit MouseCursorMonitorWin(HWND window);
+ explicit MouseCursorMonitorWin(ScreenId screen);
+ ~MouseCursorMonitorWin() override;
+
+ void Init(Callback* callback, Mode mode) override;
+ void Capture() override;
+
+ private:
+ // Get the rect of the currently selected screen, relative to the primary
+ // display's top-left. If the screen is disabled or disconnected, or any error
+ // happens, an empty rect is returned.
+ DesktopRect GetScreenRect();
+
+ HWND window_;
+ ScreenId screen_;
+
+ Callback* callback_;
+ Mode mode_;
+
+ HDC desktop_dc_;
+
+ // The last CURSORINFO (converted to MouseCursor) we have sent to the client.
+ CURSORINFO last_cursor_;
+};
+
+MouseCursorMonitorWin::MouseCursorMonitorWin(HWND window)
+ : window_(window),
+ screen_(kInvalidScreenId),
+ callback_(NULL),
+ mode_(SHAPE_AND_POSITION),
+ desktop_dc_(NULL) {
+ memset(&last_cursor_, 0, sizeof(CURSORINFO));
+}
+
+MouseCursorMonitorWin::MouseCursorMonitorWin(ScreenId screen)
+ : window_(NULL),
+ screen_(screen),
+ callback_(NULL),
+ mode_(SHAPE_AND_POSITION),
+ desktop_dc_(NULL) {
+ RTC_DCHECK_GE(screen, kFullDesktopScreenId);
+ memset(&last_cursor_, 0, sizeof(CURSORINFO));
+}
+
+MouseCursorMonitorWin::~MouseCursorMonitorWin() {
+ if (desktop_dc_)
+ ReleaseDC(NULL, desktop_dc_);
+}
+
+void MouseCursorMonitorWin::Init(Callback* callback, Mode mode) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+ RTC_DCHECK(IsGUIThread(false));
+
+ callback_ = callback;
+ mode_ = mode;
+
+ desktop_dc_ = GetDC(NULL);
+}
+
+void MouseCursorMonitorWin::Capture() {
+// TODO: Bug 1666266. Commented out to pass new tests added in bug 1634044.
+// RTC_DCHECK(IsGUIThread(false));
+ RTC_DCHECK(callback_);
+
+ CURSORINFO cursor_info;
+ cursor_info.cbSize = sizeof(CURSORINFO);
+ if (!GetCursorInfo(&cursor_info)) {
+ RTC_LOG_F(LS_ERROR) << "Unable to get cursor info. Error = "
+ << GetLastError();
+ return;
+ }
+
+ if (!IsSameCursorShape(cursor_info, last_cursor_)) {
+ // Mozilla - CURSOR_SUPPRESSED is win8 and above; so we seem not to be able to see the symbol
+ if (cursor_info.flags != CURSOR_SHOWING) {
+ // The cursor is intentionally hidden now, send an empty bitmap.
+ last_cursor_ = cursor_info;
+ callback_->OnMouseCursor(new MouseCursor(
+ new BasicDesktopFrame(DesktopSize()), DesktopVector()));
+ } else {
+ // According to MSDN https://goo.gl/u6gyuC, HCURSOR instances returned by
+ // functions other than CreateCursor do not need to be actively destroyed.
+ // And CloseHandle function (https://goo.gl/ja5ycW) does not close a
+ // cursor, so assume a HCURSOR does not need to be closed.
+ if (cursor_info.flags == 0) {
+ // Host machine does not have a hardware mouse attached, we will send a
+ // default one instead.
+ // Note, Windows automatically caches cursor resource, so we do not need
+ // to cache the result of LoadCursor.
+ cursor_info.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ }
+ std::unique_ptr<MouseCursor> cursor(
+ CreateMouseCursorFromHCursor(desktop_dc_, cursor_info.hCursor));
+ if (cursor) {
+ last_cursor_ = cursor_info;
+ callback_->OnMouseCursor(cursor.release());
+ }
+ }
+ }
+
+ if (mode_ != SHAPE_AND_POSITION)
+ return;
+
+ // CURSORINFO::ptScreenPos is in full desktop coordinate.
+ DesktopVector position(cursor_info.ptScreenPos.x, cursor_info.ptScreenPos.y);
+ bool inside = cursor_info.flags == CURSOR_SHOWING;
+
+ if (window_) {
+ DesktopRect original_rect;
+ DesktopRect cropped_rect;
+ if (!GetCroppedWindowRect(window_, /*avoid_cropping_border*/ false,
+ &cropped_rect, &original_rect)) {
+ position.set(0, 0);
+ inside = false;
+ } else {
+ if (inside) {
+ HWND windowUnderCursor = WindowFromPoint(cursor_info.ptScreenPos);
+ inside = windowUnderCursor
+ ? (window_ == GetAncestor(windowUnderCursor, GA_ROOT))
+ : false;
+ }
+ position = position.subtract(cropped_rect.top_left());
+ }
+ } else {
+ RTC_DCHECK_NE(screen_, kInvalidScreenId);
+ DesktopRect rect = GetScreenRect();
+ if (inside)
+ inside = rect.Contains(position);
+ position = position.subtract(rect.top_left());
+ }
+
+ callback_->OnMouseCursorPosition(position);
+}
+
+DesktopRect MouseCursorMonitorWin::GetScreenRect() {
+ RTC_DCHECK(IsGUIThread(false));
+ RTC_DCHECK_NE(screen_, kInvalidScreenId);
+ if (screen_ == kFullDesktopScreenId) {
+ return DesktopRect::MakeXYWH(GetSystemMetrics(SM_XVIRTUALSCREEN),
+ GetSystemMetrics(SM_YVIRTUALSCREEN),
+ GetSystemMetrics(SM_CXVIRTUALSCREEN),
+ GetSystemMetrics(SM_CYVIRTUALSCREEN));
+ }
+ DISPLAY_DEVICE device;
+ device.cb = sizeof(device);
+ BOOL result = EnumDisplayDevices(NULL, screen_, &device, 0);
+ if (!result)
+ return DesktopRect();
+
+ DEVMODE device_mode;
+ device_mode.dmSize = sizeof(device_mode);
+ device_mode.dmDriverExtra = 0;
+ result = EnumDisplaySettingsEx(device.DeviceName, ENUM_CURRENT_SETTINGS,
+ &device_mode, 0);
+ if (!result)
+ return DesktopRect();
+
+ return DesktopRect::MakeXYWH(
+ device_mode.dmPosition.x, device_mode.dmPosition.y,
+ device_mode.dmPelsWidth, device_mode.dmPelsHeight);
+}
+
+MouseCursorMonitor* MouseCursorMonitor::CreateForWindow(
+ const DesktopCaptureOptions& options,
+ WindowId window) {
+ return new MouseCursorMonitorWin(reinterpret_cast<HWND>(window));
+}
+
+MouseCursorMonitor* MouseCursorMonitor::CreateForScreen(
+ const DesktopCaptureOptions& options,
+ ScreenId screen) {
+ return new MouseCursorMonitorWin(screen);
+}
+
+std::unique_ptr<MouseCursorMonitor> MouseCursorMonitor::Create(
+ const DesktopCaptureOptions& options) {
+ return std::unique_ptr<MouseCursorMonitor>(
+ CreateForScreen(options, kFullDesktopScreenId));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/primitives_gn/moz.build b/third_party/libwebrtc/modules/desktop_capture/primitives_gn/moz.build
new file mode 100644
index 0000000000..c0ef33e2b3
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/primitives_gn/moz.build
@@ -0,0 +1,185 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+
+ ### This moz.build was AUTOMATICALLY GENERATED from a GN config, ###
+ ### DO NOT edit it by hand. ###
+
+COMPILE_FLAGS["OS_INCLUDES"] = []
+AllowCompilerWarnings()
+
+DEFINES["ABSL_ALLOCATOR_NOTHROW"] = "1"
+DEFINES["RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY"] = True
+DEFINES["RTC_ENABLE_VP9"] = True
+DEFINES["WEBRTC_ENABLE_PROTOBUF"] = "0"
+DEFINES["WEBRTC_LIBRARY_IMPL"] = True
+DEFINES["WEBRTC_MOZILLA_BUILD"] = True
+DEFINES["WEBRTC_NON_STATIC_TRACE_EVENT_HANDLERS"] = "0"
+
+FINAL_LIBRARY = "webrtc"
+
+
+LOCAL_INCLUDES += [
+ "!/ipc/ipdl/_ipdlheaders",
+ "/ipc/chromium/src",
+ "/media/libyuv/",
+ "/media/libyuv/libyuv/include/",
+ "/third_party/libwebrtc/",
+ "/third_party/libwebrtc/third_party/abseil-cpp/",
+ "/tools/profiler/public"
+]
+
+UNIFIED_SOURCES += [
+ "/third_party/libwebrtc/modules/desktop_capture/desktop_frame.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/desktop_geometry.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/desktop_region.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/shared_desktop_frame.cc",
+ "/third_party/libwebrtc/modules/desktop_capture/shared_memory.cc"
+]
+
+if not CONFIG["MOZ_DEBUG"]:
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "0"
+ DEFINES["NDEBUG"] = True
+ DEFINES["NVALGRIND"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1":
+
+ DEFINES["DYNAMIC_ANNOTATIONS_ENABLED"] = "1"
+
+if CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_MAC"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_LIBCPP_HAS_NO_ALIGNED_ALLOCATION"] = True
+ DEFINES["__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES"] = "0"
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_NSS_CERTS"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_UDEV"] = True
+ DEFINES["WEBRTC_LINUX"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["USE_GLIB"] = "1"
+ DEFINES["USE_OZONE"] = "1"
+ DEFINES["USE_X11"] = "1"
+ DEFINES["WEBRTC_BSD"] = True
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_POSIX"] = True
+ DEFINES["_FILE_OFFSET_BITS"] = "64"
+ DEFINES["_LARGEFILE64_SOURCE"] = True
+ DEFINES["_LARGEFILE_SOURCE"] = True
+ DEFINES["__STDC_CONSTANT_MACROS"] = True
+ DEFINES["__STDC_FORMAT_MACROS"] = True
+
+if CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["CERT_CHAIN_PARA_HAS_EXTRA_FIELDS"] = True
+ DEFINES["NOMINMAX"] = True
+ DEFINES["NTDDI_VERSION"] = "0x0A000000"
+ DEFINES["PSAPI_VERSION"] = "2"
+ DEFINES["UNICODE"] = True
+ DEFINES["USE_AURA"] = "1"
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_WIN"] = True
+ DEFINES["WIN32"] = True
+ DEFINES["WIN32_LEAN_AND_MEAN"] = True
+ DEFINES["WINAPI_FAMILY"] = "WINAPI_FAMILY_DESKTOP_APP"
+ DEFINES["WINVER"] = "0x0A00"
+ DEFINES["_ATL_NO_OPENGL"] = True
+ DEFINES["_CRT_RAND_S"] = True
+ DEFINES["_CRT_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_ENABLE_EXTENDED_ALIGNED_STORAGE"] = True
+ DEFINES["_HAS_EXCEPTIONS"] = "0"
+ DEFINES["_HAS_NODISCARD"] = True
+ DEFINES["_SCL_SECURE_NO_DEPRECATE"] = True
+ DEFINES["_SECURE_ATL"] = True
+ DEFINES["_UNICODE"] = True
+ DEFINES["_WIN32_WINNT"] = "0x0A00"
+ DEFINES["_WINDOWS"] = True
+ DEFINES["__STD_C"] = True
+
+if CONFIG["CPU_ARCH"] == "aarch64":
+
+ DEFINES["WEBRTC_ARCH_ARM64"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+
+if CONFIG["CPU_ARCH"] == "arm":
+
+ CXXFLAGS += [
+ "-mfpu=neon"
+ ]
+
+ DEFINES["WEBRTC_ARCH_ARM"] = True
+ DEFINES["WEBRTC_ARCH_ARM_V7"] = True
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["WEBRTC_HAS_NEON"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "ppc64":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Darwin":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "OpenBSD":
+
+ DEFINES["_DEBUG"] = True
+
+if CONFIG["MOZ_DEBUG"] == "1" and CONFIG["OS_TARGET"] == "WINNT":
+
+ DEFINES["_HAS_ITERATOR_DEBUGGING"] = "0"
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["WEBRTC_ENABLE_AVX2"] = True
+ DEFINES["_GNU_SOURCE"] = True
+
+if CONFIG["CPU_ARCH"] == "aarch64" and CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "arm" and CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "x86" and CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+if CONFIG["CPU_ARCH"] == "x86_64" and CONFIG["MOZ_X11"] == "1" and CONFIG["OS_TARGET"] == "Linux":
+
+ DEFINES["USE_X11"] = "1"
+
+Library("primitives_gn")
diff --git a/third_party/libwebrtc/modules/desktop_capture/resolution_tracker.cc b/third_party/libwebrtc/modules/desktop_capture/resolution_tracker.cc
new file mode 100644
index 0000000000..9639d627fa
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/resolution_tracker.cc
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/resolution_tracker.h"
+
+namespace webrtc {
+
+bool ResolutionTracker::SetResolution(DesktopSize size) {
+ if (!initialized_) {
+ initialized_ = true;
+ last_size_ = size;
+ return false;
+ }
+
+ if (last_size_.equals(size)) {
+ return false;
+ }
+
+ last_size_ = size;
+ return true;
+}
+
+void ResolutionTracker::Reset() {
+ initialized_ = false;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/resolution_tracker.h b/third_party/libwebrtc/modules/desktop_capture/resolution_tracker.h
new file mode 100644
index 0000000000..8fe9d61862
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/resolution_tracker.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_RESOLUTION_TRACKER_H_
+#define MODULES_DESKTOP_CAPTURE_RESOLUTION_TRACKER_H_
+
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+class ResolutionTracker final {
+ public:
+ // Sets the resolution to `size`. Returns true if a previous size was recorded
+ // and differs from `size`.
+ bool SetResolution(DesktopSize size);
+
+ // Resets to the initial state.
+ void Reset();
+
+ private:
+ DesktopSize last_size_;
+ bool initialized_ = false;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_RESOLUTION_TRACKER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/rgba_color.cc b/third_party/libwebrtc/modules/desktop_capture/rgba_color.cc
new file mode 100644
index 0000000000..362928a474
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/rgba_color.cc
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/rgba_color.h"
+
+#include "rtc_base/system/arch.h"
+
+namespace webrtc {
+
+namespace {
+
+bool AlphaEquals(uint8_t i, uint8_t j) {
+ // On Linux and Windows 8 or early version, '0' was returned for alpha channel
+ // from capturer APIs, on Windows 10, '255' was returned. So a workaround is
+ // to treat 0 as 255.
+ return i == j || ((i == 0 || i == 255) && (j == 0 || j == 255));
+}
+
+} // namespace
+
+RgbaColor::RgbaColor(uint8_t blue, uint8_t green, uint8_t red, uint8_t alpha) {
+ this->blue = blue;
+ this->green = green;
+ this->red = red;
+ this->alpha = alpha;
+}
+
+RgbaColor::RgbaColor(uint8_t blue, uint8_t green, uint8_t red)
+ : RgbaColor(blue, green, red, 0xff) {}
+
+RgbaColor::RgbaColor(const uint8_t* bgra)
+ : RgbaColor(bgra[0], bgra[1], bgra[2], bgra[3]) {}
+
+RgbaColor::RgbaColor(uint32_t bgra)
+ : RgbaColor(reinterpret_cast<uint8_t*>(&bgra)) {}
+
+bool RgbaColor::operator==(const RgbaColor& right) const {
+ return blue == right.blue && green == right.green && red == right.red &&
+ AlphaEquals(alpha, right.alpha);
+}
+
+bool RgbaColor::operator!=(const RgbaColor& right) const {
+ return !(*this == right);
+}
+
+uint32_t RgbaColor::ToUInt32() const {
+#if defined(WEBRTC_ARCH_LITTLE_ENDIAN)
+ return blue | (green << 8) | (red << 16) | (alpha << 24);
+#else
+ return (blue << 24) | (green << 16) | (red << 8) | alpha;
+#endif
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/rgba_color.h b/third_party/libwebrtc/modules/desktop_capture/rgba_color.h
new file mode 100644
index 0000000000..2b13430a45
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/rgba_color.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_RGBA_COLOR_H_
+#define MODULES_DESKTOP_CAPTURE_RGBA_COLOR_H_
+
+#include <stdint.h>
+
+#include "modules/desktop_capture/desktop_frame.h"
+
+namespace webrtc {
+
+// A four-byte structure to store a color in BGRA format. This structure also
+// provides functions to be created from uint8_t array, say,
+// DesktopFrame::data(). It always uses BGRA order for internal storage to match
+// DesktopFrame::data().
+struct RgbaColor final {
+ // Creates a color with BGRA channels.
+ RgbaColor(uint8_t blue, uint8_t green, uint8_t red, uint8_t alpha);
+
+ // Creates a color with BGR channels, and set alpha channel to 255 (opaque).
+ RgbaColor(uint8_t blue, uint8_t green, uint8_t red);
+
+ // Creates a color from four-byte in BGRA order, i.e. DesktopFrame::data().
+ explicit RgbaColor(const uint8_t* bgra);
+
+ // Creates a color from BGRA channels in a uint format. Consumers should make
+ // sure the memory order of the uint32_t is always BGRA from left to right, no
+ // matter the system endian. This function creates an equivalent RgbaColor
+ // instance from the ToUInt32() result of another RgbaColor instance.
+ explicit RgbaColor(uint32_t bgra);
+
+ // Returns true if `this` and `right` is the same color.
+ bool operator==(const RgbaColor& right) const;
+
+ // Returns true if `this` and `right` are different colors.
+ bool operator!=(const RgbaColor& right) const;
+
+ uint32_t ToUInt32() const;
+
+ uint8_t blue;
+ uint8_t green;
+ uint8_t red;
+ uint8_t alpha;
+};
+static_assert(
+ DesktopFrame::kBytesPerPixel == sizeof(RgbaColor),
+ "A pixel in DesktopFrame should be safe to be represented by a RgbaColor");
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_RGBA_COLOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/rgba_color_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/rgba_color_unittest.cc
new file mode 100644
index 0000000000..48e40594b1
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/rgba_color_unittest.cc
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/rgba_color.h"
+
+#include <cstdint>
+#include <vector>
+
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(RgbaColorTest, ConvertFromAndToUInt32) {
+ static const std::vector<uint32_t> cases{
+ 0, 1000, 2693, 3725, 4097, 12532,
+ 19902, 27002, 27723, 30944, 65535, 65536,
+ 231194, 255985, 322871, 883798, 9585200, 12410056,
+ 12641940, 30496970, 105735668, 110117847, 482769275, 542368468,
+ 798173396, 2678656711, 3231043200, UINT32_MAX,
+ };
+
+ for (uint32_t value : cases) {
+ RgbaColor left(value);
+ ASSERT_EQ(left.ToUInt32(), value);
+ RgbaColor right(left);
+ ASSERT_EQ(left.ToUInt32(), right.ToUInt32());
+ }
+}
+
+TEST(RgbaColorTest, AlphaChannelEquality) {
+ RgbaColor left(10, 10, 10, 0);
+ RgbaColor right(10, 10, 10, 255);
+ ASSERT_EQ(left, right);
+ right.alpha = 128;
+ ASSERT_NE(left, right);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capture_frame_queue.h b/third_party/libwebrtc/modules/desktop_capture/screen_capture_frame_queue.h
new file mode 100644
index 0000000000..46e19da77e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_capture_frame_queue.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURE_FRAME_QUEUE_H_
+#define MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURE_FRAME_QUEUE_H_
+
+#include <memory>
+
+namespace webrtc {
+
+// Represents a queue of reusable video frames. Provides access to the 'current'
+// frame - the frame that the caller is working with at the moment, and to the
+// 'previous' frame - the predecessor of the current frame swapped by
+// MoveToNextFrame() call, if any.
+//
+// The caller is expected to (re)allocate frames if current_frame() returns
+// NULL. The caller can mark all frames in the queue for reallocation (when,
+// say, frame dimensions change). The queue records which frames need updating
+// which the caller can query.
+//
+// Frame consumer is expected to never hold more than kQueueLength frames
+// created by this function and it should release the earliest one before trying
+// to capture a new frame (i.e. before MoveToNextFrame() is called).
+template <typename FrameType>
+class ScreenCaptureFrameQueue {
+ public:
+ ScreenCaptureFrameQueue() = default;
+ ~ScreenCaptureFrameQueue() = default;
+
+ ScreenCaptureFrameQueue(const ScreenCaptureFrameQueue&) = delete;
+ ScreenCaptureFrameQueue& operator=(const ScreenCaptureFrameQueue&) = delete;
+
+ // Moves to the next frame in the queue, moving the 'current' frame to become
+ // the 'previous' one.
+ void MoveToNextFrame() { current_ = (current_ + 1) % kQueueLength; }
+
+ // Replaces the current frame with a new one allocated by the caller. The
+ // existing frame (if any) is destroyed. Takes ownership of `frame`.
+ void ReplaceCurrentFrame(std::unique_ptr<FrameType> frame) {
+ frames_[current_] = std::move(frame);
+ }
+
+ // Marks all frames obsolete and resets the previous frame pointer. No
+ // frames are freed though as the caller can still access them.
+ void Reset() {
+ for (int i = 0; i < kQueueLength; i++) {
+ frames_[i].reset();
+ }
+ current_ = 0;
+ }
+
+ FrameType* current_frame() const { return frames_[current_].get(); }
+
+ FrameType* previous_frame() const {
+ return frames_[(current_ + kQueueLength - 1) % kQueueLength].get();
+ }
+
+ private:
+ // Index of the current frame.
+ int current_ = 0;
+
+ static const int kQueueLength = 2;
+ std::unique_ptr<FrameType> frames_[kQueueLength];
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURE_FRAME_QUEUE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_darwin.mm b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_darwin.mm
new file mode 100644
index 0000000000..d5a7bb0522
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_darwin.mm
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "modules/desktop_capture/mac/screen_capturer_mac.h"
+
+namespace webrtc {
+
+// static
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options) {
+ if (!options.configuration_monitor()) {
+ return nullptr;
+ }
+
+ std::unique_ptr<ScreenCapturerMac> capturer(new ScreenCapturerMac(
+ options.configuration_monitor(), options.detect_updated_region(), options.allow_iosurface()));
+ if (!capturer.get()->Init()) {
+ return nullptr;
+ }
+
+ return capturer;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_fuchsia.cc b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_fuchsia.cc
new file mode 100644
index 0000000000..c0ad841c05
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_fuchsia.cc
@@ -0,0 +1,415 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/screen_capturer_fuchsia.h"
+
+#include <fuchsia/sysmem/cpp/fidl.h>
+#include <fuchsia/ui/composition/cpp/fidl.h>
+#include <fuchsia/ui/scenic/cpp/fidl.h>
+#include <lib/sys/cpp/component_context.h>
+
+#include <algorithm>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/fallback_desktop_capturer_wrapper.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/divide_round.h"
+#include "rtc_base/time_utils.h"
+
+namespace webrtc {
+
+namespace {
+
+static constexpr uint32_t kMinBufferCount = 2;
+static constexpr uint32_t kFuchsiaBytesPerPixel = 4;
+static constexpr DesktopCapturer::SourceId kFuchsiaScreenId = 1;
+// 500 milliseconds
+static constexpr zx::duration kEventDelay = zx::msec(500);
+static constexpr fuchsia::sysmem::ColorSpaceType kSRGBColorSpace =
+ fuchsia::sysmem::ColorSpaceType::SRGB;
+static constexpr fuchsia::sysmem::PixelFormatType kBGRA32PixelFormatType =
+ fuchsia::sysmem::PixelFormatType::BGRA32;
+
+// Round |value| up to the closest multiple of |multiple|
+size_t RoundUpToMultiple(size_t value, size_t multiple) {
+ return DivideRoundUp(value, multiple) * multiple;
+}
+
+} // namespace
+
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options) {
+ if (ScreenCapturerFuchsia::CheckRequirements()) {
+ std::unique_ptr<ScreenCapturerFuchsia> capturer(
+ new ScreenCapturerFuchsia());
+ return capturer;
+ }
+ return nullptr;
+}
+
+ScreenCapturerFuchsia::ScreenCapturerFuchsia()
+ : component_context_(
+ sys::ComponentContext::CreateAndServeOutgoingDirectory()) {
+ RTC_DCHECK(CheckRequirements());
+}
+
+ScreenCapturerFuchsia::~ScreenCapturerFuchsia() {
+ // unmap virtual memory mapped pointers
+ uint32_t virt_mem_bytes =
+ buffer_collection_info_.settings.buffer_settings.size_bytes;
+ for (uint32_t buffer_index = 0;
+ buffer_index < buffer_collection_info_.buffer_count; buffer_index++) {
+ uintptr_t address =
+ reinterpret_cast<uintptr_t>(virtual_memory_mapped_addrs_[buffer_index]);
+ zx_status_t status = zx::vmar::root_self()->unmap(address, virt_mem_bytes);
+ RTC_DCHECK(status == ZX_OK);
+ }
+}
+
+// TODO(fxbug.dev/100303): Remove this function when Flatland is the only API.
+bool ScreenCapturerFuchsia::CheckRequirements() {
+ std::unique_ptr<sys::ComponentContext> component_context =
+ sys::ComponentContext::CreateAndServeOutgoingDirectory();
+ fuchsia::ui::scenic::ScenicSyncPtr scenic;
+ zx_status_t status = component_context->svc()->Connect(scenic.NewRequest());
+ if (status != ZX_OK) {
+ RTC_LOG(LS_ERROR) << "Failed to connect to Scenic: " << status;
+ return false;
+ }
+
+ bool scenic_uses_flatland = false;
+ scenic->UsesFlatland(&scenic_uses_flatland);
+ if (!scenic_uses_flatland) {
+ RTC_LOG(LS_ERROR) << "Screen capture not supported without Flatland.";
+ }
+
+ return scenic_uses_flatland;
+}
+
+void ScreenCapturerFuchsia::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+ callback_ = callback;
+
+ fatal_error_ = false;
+
+ SetupBuffers();
+}
+
+void ScreenCapturerFuchsia::CaptureFrame() {
+ if (fatal_error_) {
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ zx::event event;
+ zx::event dup;
+ zx_status_t status = zx::event::create(0, &event);
+ if (status != ZX_OK) {
+ RTC_LOG(LS_ERROR) << "Failed to create event: " << status;
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+ event.duplicate(ZX_RIGHT_SAME_RIGHTS, &dup);
+
+ fuchsia::ui::composition::GetNextFrameArgs next_frame_args;
+ next_frame_args.set_event(std::move(dup));
+
+ fuchsia::ui::composition::ScreenCapture_GetNextFrame_Result result;
+ screen_capture_->GetNextFrame(std::move(next_frame_args), &result);
+ if (result.is_err()) {
+ RTC_LOG(LS_ERROR) << "fuchsia.ui.composition.GetNextFrame() failed: "
+ << result.err() << "\n";
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+
+ status = event.wait_one(ZX_EVENT_SIGNALED, zx::deadline_after(kEventDelay),
+ nullptr);
+ if (status != ZX_OK) {
+ RTC_LOG(LS_ERROR) << "Timed out waiting for ScreenCapture to render frame: "
+ << status;
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+ uint32_t buffer_index = result.response().buffer_id();
+
+ // TODO(bugs.webrtc.org/14097): Use SharedMemoryDesktopFrame and
+ // ScreenCaptureFrameQueue
+ std::unique_ptr<BasicDesktopFrame> frame(
+ new BasicDesktopFrame(DesktopSize(width_, height_)));
+
+ uint32_t pixels_per_row = GetPixelsPerRow(
+ buffer_collection_info_.settings.image_format_constraints);
+ uint32_t stride = kFuchsiaBytesPerPixel * pixels_per_row;
+ frame->CopyPixelsFrom(virtual_memory_mapped_addrs_[buffer_index], stride,
+ DesktopRect::MakeWH(width_, height_));
+
+ fuchsia::ui::composition::ScreenCapture_ReleaseFrame_Result release_result;
+ screen_capture_->ReleaseFrame(buffer_index, &release_result);
+ if (release_result.is_err()) {
+ RTC_LOG(LS_ERROR) << "fuchsia.ui.composition.ReleaseFrame() failed: "
+ << release_result.err();
+ }
+
+ int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec;
+ frame->set_capture_time_ms(capture_time_ms);
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+}
+
+bool ScreenCapturerFuchsia::GetSourceList(SourceList* screens) {
+ RTC_DCHECK(screens->size() == 0);
+ // Fuchsia only supports single monitor display at this point
+ screens->push_back({kFuchsiaScreenId, std::string("Fuchsia monitor")});
+ return true;
+}
+
+bool ScreenCapturerFuchsia::SelectSource(SourceId id) {
+ if (id == kFuchsiaScreenId || id == kFullDesktopScreenId) {
+ return true;
+ }
+ return false;
+}
+
+fuchsia::sysmem::BufferCollectionConstraints
+ScreenCapturerFuchsia::GetBufferConstraints() {
+ fuchsia::sysmem::BufferCollectionConstraints constraints;
+ constraints.usage.cpu =
+ fuchsia::sysmem::cpuUsageRead | fuchsia::sysmem::cpuUsageWrite;
+ constraints.min_buffer_count = kMinBufferCount;
+
+ constraints.has_buffer_memory_constraints = true;
+ constraints.buffer_memory_constraints.ram_domain_supported = true;
+ constraints.buffer_memory_constraints.cpu_domain_supported = true;
+
+ constraints.image_format_constraints_count = 1;
+ fuchsia::sysmem::ImageFormatConstraints& image_constraints =
+ constraints.image_format_constraints[0];
+ image_constraints.color_spaces_count = 1;
+ image_constraints.color_space[0] =
+ fuchsia::sysmem::ColorSpace{.type = kSRGBColorSpace};
+ image_constraints.pixel_format.type = kBGRA32PixelFormatType;
+ image_constraints.pixel_format.has_format_modifier = true;
+ image_constraints.pixel_format.format_modifier.value =
+ fuchsia::sysmem::FORMAT_MODIFIER_LINEAR;
+
+ image_constraints.required_min_coded_width = width_;
+ image_constraints.required_min_coded_height = height_;
+ image_constraints.required_max_coded_width = width_;
+ image_constraints.required_max_coded_height = height_;
+
+ image_constraints.bytes_per_row_divisor = kFuchsiaBytesPerPixel;
+
+ return constraints;
+}
+
+void ScreenCapturerFuchsia::SetupBuffers() {
+ fuchsia::ui::scenic::ScenicSyncPtr scenic;
+ zx_status_t status = component_context_->svc()->Connect(scenic.NewRequest());
+ if (status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR) << "Failed to connect to Scenic: " << status;
+ return;
+ }
+
+ fuchsia::ui::gfx::DisplayInfo display_info;
+ status = scenic->GetDisplayInfo(&display_info);
+ if (status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR) << "Failed to connect to get display dimensions: "
+ << status;
+ return;
+ }
+ width_ = display_info.width_in_px;
+ height_ = display_info.height_in_px;
+
+ status = component_context_->svc()->Connect(sysmem_allocator_.NewRequest());
+ if (status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR) << "Failed to connect to Sysmem Allocator: " << status;
+ return;
+ }
+
+ fuchsia::sysmem::BufferCollectionTokenSyncPtr sysmem_token;
+ status =
+ sysmem_allocator_->AllocateSharedCollection(sysmem_token.NewRequest());
+ if (status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR)
+ << "fuchsia.sysmem.Allocator.AllocateSharedCollection() failed: "
+ << status;
+ return;
+ }
+
+ fuchsia::sysmem::BufferCollectionTokenSyncPtr flatland_token;
+ status = sysmem_token->Duplicate(ZX_RIGHT_SAME_RIGHTS,
+ flatland_token.NewRequest());
+ if (status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR)
+ << "fuchsia.sysmem.BufferCollectionToken.Duplicate() failed: "
+ << status;
+ return;
+ }
+
+ status = sysmem_token->Sync();
+ if (status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR) << "fuchsia.sysmem.BufferCollectionToken.Sync() failed: "
+ << status;
+ return;
+ }
+
+ status = sysmem_allocator_->BindSharedCollection(std::move(sysmem_token),
+ collection_.NewRequest());
+ if (status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR)
+ << "fuchsia.sysmem.Allocator.BindSharedCollection() failed: " << status;
+ return;
+ }
+
+ status = collection_->SetConstraints(/*has_constraints=*/true,
+ GetBufferConstraints());
+ if (status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR)
+ << "fuchsia.sysmem.BufferCollection.SetConstraints() failed: "
+ << status;
+ return;
+ }
+
+ fuchsia::ui::composition::BufferCollectionImportToken import_token;
+ fuchsia::ui::composition::BufferCollectionExportToken export_token;
+ status = zx::eventpair::create(0, &export_token.value, &import_token.value);
+ if (status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR)
+ << "Failed to create BufferCollection import and export tokens: "
+ << status;
+ return;
+ }
+
+ status = component_context_->svc()->Connect(flatland_allocator_.NewRequest());
+ if (status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR) << "Failed to connect to Flatland Allocator: " << status;
+ return;
+ }
+
+ fuchsia::ui::composition::RegisterBufferCollectionArgs buffer_collection_args;
+ buffer_collection_args.set_export_token(std::move(export_token));
+ buffer_collection_args.set_buffer_collection_token(std::move(flatland_token));
+ buffer_collection_args.set_usage(
+ fuchsia::ui::composition::RegisterBufferCollectionUsage::SCREENSHOT);
+
+ fuchsia::ui::composition::Allocator_RegisterBufferCollection_Result
+ buffer_collection_result;
+ flatland_allocator_->RegisterBufferCollection(
+ std::move(buffer_collection_args), &buffer_collection_result);
+ if (buffer_collection_result.is_err()) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR) << "fuchsia.ui.composition.Allocator."
+ "RegisterBufferCollection() failed.";
+ return;
+ }
+
+ zx_status_t allocation_status;
+ status = collection_->WaitForBuffersAllocated(&allocation_status,
+ &buffer_collection_info_);
+ if (status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR) << "Failed to wait for buffer collection info: "
+ << status;
+ return;
+ }
+ if (allocation_status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR) << "Failed to allocate buffer collection: " << status;
+ return;
+ }
+ status = collection_->Close();
+ if (status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR) << "Failed to close buffer collection token: " << status;
+ return;
+ }
+
+ status = component_context_->svc()->Connect(screen_capture_.NewRequest());
+ if (status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR) << "Failed to connect to Screen Capture: " << status;
+ return;
+ }
+
+ // Configure buffers in ScreenCapture client.
+ fuchsia::ui::composition::ScreenCaptureConfig configure_args;
+ configure_args.set_import_token(std::move(import_token));
+ configure_args.set_buffer_count(buffer_collection_info_.buffer_count);
+ configure_args.set_size({width_, height_});
+
+ fuchsia::ui::composition::ScreenCapture_Configure_Result configure_result;
+ screen_capture_->Configure(std::move(configure_args), &configure_result);
+ if (configure_result.is_err()) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR)
+ << "fuchsia.ui.composition.ScreenCapture.Configure() failed: "
+ << configure_result.err();
+ return;
+ }
+
+ // We have a collection of virtual memory objects which the ScreenCapture
+ // client will write the frame data to when requested. We map each of these
+ // onto a pointer stored in virtual_memory_mapped_addrs_ which we can use to
+ // access this data.
+ uint32_t virt_mem_bytes =
+ buffer_collection_info_.settings.buffer_settings.size_bytes;
+ RTC_DCHECK(virt_mem_bytes > 0);
+ for (uint32_t buffer_index = 0;
+ buffer_index < buffer_collection_info_.buffer_count; buffer_index++) {
+ const zx::vmo& virt_mem = buffer_collection_info_.buffers[buffer_index].vmo;
+ virtual_memory_mapped_addrs_[buffer_index] = nullptr;
+ auto status = zx::vmar::root_self()->map(
+ ZX_VM_PERM_READ, /*vmar_offset*/ 0, virt_mem,
+ /*vmo_offset*/ 0, virt_mem_bytes,
+ reinterpret_cast<uintptr_t*>(
+ &virtual_memory_mapped_addrs_[buffer_index]));
+ if (status != ZX_OK) {
+ fatal_error_ = true;
+ RTC_LOG(LS_ERROR) << "Failed to map virtual memory: " << status;
+ return;
+ }
+ }
+}
+
+uint32_t ScreenCapturerFuchsia::GetPixelsPerRow(
+ const fuchsia::sysmem::ImageFormatConstraints& constraints) {
+ uint32_t stride = RoundUpToMultiple(
+ std::max(constraints.min_bytes_per_row, width_ * kFuchsiaBytesPerPixel),
+ constraints.bytes_per_row_divisor);
+ uint32_t pixels_per_row = stride / kFuchsiaBytesPerPixel;
+
+ return pixels_per_row;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_fuchsia.h b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_fuchsia.h
new file mode 100644
index 0000000000..444930963f
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_fuchsia.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2022 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_FUCHSIA_H_
+#define MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_FUCHSIA_H_
+
+#include <fuchsia/sysmem/cpp/fidl.h>
+#include <fuchsia/ui/composition/cpp/fidl.h>
+#include <lib/sys/cpp/component_context.h>
+
+#include <memory>
+#include <unordered_map>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+
+namespace webrtc {
+
+class ScreenCapturerFuchsia final : public DesktopCapturer {
+ public:
+ ScreenCapturerFuchsia();
+ ~ScreenCapturerFuchsia() override;
+
+ static bool CheckRequirements();
+
+ // DesktopCapturer interface.
+ void Start(Callback* callback) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* screens) override;
+ bool SelectSource(SourceId id) override;
+
+ private:
+ fuchsia::sysmem::BufferCollectionConstraints GetBufferConstraints();
+ void SetupBuffers();
+ uint32_t GetPixelsPerRow(
+ const fuchsia::sysmem::ImageFormatConstraints& constraints);
+
+ Callback* callback_ = nullptr;
+
+ std::unique_ptr<sys::ComponentContext> component_context_;
+ fuchsia::sysmem::AllocatorSyncPtr sysmem_allocator_;
+ fuchsia::ui::composition::AllocatorSyncPtr flatland_allocator_;
+ fuchsia::ui::composition::ScreenCaptureSyncPtr screen_capture_;
+ fuchsia::sysmem::BufferCollectionSyncPtr collection_;
+ fuchsia::sysmem::BufferCollectionInfo_2 buffer_collection_info_;
+ std::unordered_map<uint32_t, uint8_t*> virtual_memory_mapped_addrs_;
+
+ bool fatal_error_;
+
+ // Dimensions of the screen we are capturing
+ uint32_t width_;
+ uint32_t height_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_FUCHSIA_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_helper.cc b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_helper.cc
new file mode 100644
index 0000000000..f8261a90b0
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_helper.cc
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/screen_capturer_helper.h"
+
+
+namespace webrtc {
+
+void ScreenCapturerHelper::ClearInvalidRegion() {
+ MutexLock scoped_invalid_region_lock(&invalid_region_mutex_);
+ invalid_region_.Clear();
+}
+
+void ScreenCapturerHelper::InvalidateRegion(
+ const DesktopRegion& invalid_region) {
+ MutexLock scoped_invalid_region_lock(&invalid_region_mutex_);
+ invalid_region_.AddRegion(invalid_region);
+}
+
+void ScreenCapturerHelper::InvalidateScreen(const DesktopSize& size) {
+ MutexLock scoped_invalid_region_lock(&invalid_region_mutex_);
+ invalid_region_.AddRect(DesktopRect::MakeSize(size));
+}
+
+void ScreenCapturerHelper::TakeInvalidRegion(DesktopRegion* invalid_region) {
+ invalid_region->Clear();
+
+ {
+ MutexLock scoped_invalid_region_lock(&invalid_region_mutex_);
+ invalid_region->Swap(&invalid_region_);
+ }
+
+ if (log_grid_size_ > 0) {
+ DesktopRegion expanded_region;
+ ExpandToGrid(*invalid_region, log_grid_size_, &expanded_region);
+ expanded_region.Swap(invalid_region);
+
+ invalid_region->IntersectWith(DesktopRect::MakeSize(size_most_recent_));
+ }
+}
+
+void ScreenCapturerHelper::SetLogGridSize(int log_grid_size) {
+ log_grid_size_ = log_grid_size;
+}
+
+const DesktopSize& ScreenCapturerHelper::size_most_recent() const {
+ return size_most_recent_;
+}
+
+void ScreenCapturerHelper::set_size_most_recent(const DesktopSize& size) {
+ size_most_recent_ = size;
+}
+
+// Returns the largest multiple of `n` that is <= `x`.
+// `n` must be a power of 2. `nMask` is ~(`n` - 1).
+static int DownToMultiple(int x, int nMask) {
+ return (x & nMask);
+}
+
+// Returns the smallest multiple of `n` that is >= `x`.
+// `n` must be a power of 2. `nMask` is ~(`n` - 1).
+static int UpToMultiple(int x, int n, int nMask) {
+ return ((x + n - 1) & nMask);
+}
+
+void ScreenCapturerHelper::ExpandToGrid(const DesktopRegion& region,
+ int log_grid_size,
+ DesktopRegion* result) {
+ RTC_DCHECK_GE(log_grid_size, 1);
+ int grid_size = 1 << log_grid_size;
+ int grid_size_mask = ~(grid_size - 1);
+
+ result->Clear();
+ for (DesktopRegion::Iterator it(region); !it.IsAtEnd(); it.Advance()) {
+ int left = DownToMultiple(it.rect().left(), grid_size_mask);
+ int right = UpToMultiple(it.rect().right(), grid_size, grid_size_mask);
+ int top = DownToMultiple(it.rect().top(), grid_size_mask);
+ int bottom = UpToMultiple(it.rect().bottom(), grid_size, grid_size_mask);
+ result->AddRect(DesktopRect::MakeLTRB(left, top, right, bottom));
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_helper.h b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_helper.h
new file mode 100644
index 0000000000..cd7fa689c0
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_helper.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_HELPER_H_
+#define MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_HELPER_H_
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/thread_annotations.h"
+
+namespace webrtc {
+
+// ScreenCapturerHelper is intended to be used by an implementation of the
+// ScreenCapturer interface. It maintains a thread-safe invalid region, and
+// the size of the most recently captured screen, on behalf of the
+// ScreenCapturer that owns it.
+class ScreenCapturerHelper {
+ public:
+ ScreenCapturerHelper() = default;
+ ~ScreenCapturerHelper() = default;
+
+ ScreenCapturerHelper(const ScreenCapturerHelper&) = delete;
+ ScreenCapturerHelper& operator=(const ScreenCapturerHelper&) = delete;
+
+ // Clear out the invalid region.
+ void ClearInvalidRegion();
+
+ // Invalidate the specified region.
+ void InvalidateRegion(const DesktopRegion& invalid_region);
+
+ // Invalidate the entire screen, of a given size.
+ void InvalidateScreen(const DesktopSize& size);
+
+ // Copies current invalid region to `invalid_region` clears invalid region
+ // storage for the next frame.
+ void TakeInvalidRegion(DesktopRegion* invalid_region);
+
+ // Access the size of the most recently captured screen.
+ const DesktopSize& size_most_recent() const;
+ void set_size_most_recent(const DesktopSize& size);
+
+ // Lossy compression can result in color values leaking between pixels in one
+ // block. If part of a block changes, then unchanged parts of that block can
+ // be changed in the compressed output. So we need to re-render an entire
+ // block whenever part of the block changes.
+ //
+ // If `log_grid_size` is >= 1, then this function makes TakeInvalidRegion()
+ // produce an invalid region expanded so that its vertices lie on a grid of
+ // size 2 ^ `log_grid_size`. The expanded region is then clipped to the size
+ // of the most recently captured screen, as previously set by
+ // set_size_most_recent().
+ // If `log_grid_size` is <= 0, then the invalid region is not expanded.
+ void SetLogGridSize(int log_grid_size);
+
+ // Expands a region so that its vertices all lie on a grid.
+ // The grid size must be >= 2, so `log_grid_size` must be >= 1.
+ static void ExpandToGrid(const DesktopRegion& region,
+ int log_grid_size,
+ DesktopRegion* result);
+
+ private:
+ // A region that has been manually invalidated (through InvalidateRegion).
+ // These will be returned as dirty_region in the capture data during the next
+ // capture.
+ DesktopRegion invalid_region_ RTC_GUARDED_BY(invalid_region_mutex_);
+
+ // A lock protecting `invalid_region_` across threads.
+ Mutex invalid_region_mutex_;
+
+ // The size of the most recently captured screen.
+ DesktopSize size_most_recent_;
+
+ // The log (base 2) of the size of the grid to which the invalid region is
+ // expanded.
+ // If the value is <= 0, then the invalid region is not expanded to a grid.
+ int log_grid_size_ = 0;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_SCREEN_CAPTURER_HELPER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_helper_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_helper_unittest.cc
new file mode 100644
index 0000000000..165bbe42de
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_helper_unittest.cc
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/screen_capturer_helper.h"
+
+#include "test/gtest.h"
+
+namespace webrtc {
+
+class ScreenCapturerHelperTest : public ::testing::Test {
+ protected:
+ ScreenCapturerHelper capturer_helper_;
+};
+
+TEST_F(ScreenCapturerHelperTest, ClearInvalidRegion) {
+ DesktopRegion region(DesktopRect::MakeXYWH(1, 2, 3, 4));
+ capturer_helper_.InvalidateRegion(region);
+ capturer_helper_.ClearInvalidRegion();
+ capturer_helper_.TakeInvalidRegion(&region);
+ EXPECT_TRUE(region.is_empty());
+}
+
+TEST_F(ScreenCapturerHelperTest, InvalidateRegion) {
+ DesktopRegion region;
+ capturer_helper_.TakeInvalidRegion(&region);
+ EXPECT_TRUE(region.is_empty());
+
+ region.SetRect(DesktopRect::MakeXYWH(1, 2, 3, 4));
+ capturer_helper_.InvalidateRegion(region);
+ capturer_helper_.TakeInvalidRegion(&region);
+ EXPECT_TRUE(DesktopRegion(DesktopRect::MakeXYWH(1, 2, 3, 4)).Equals(region));
+
+ capturer_helper_.InvalidateRegion(
+ DesktopRegion(DesktopRect::MakeXYWH(1, 2, 3, 4)));
+ capturer_helper_.InvalidateRegion(
+ DesktopRegion(DesktopRect::MakeXYWH(4, 2, 3, 4)));
+ capturer_helper_.TakeInvalidRegion(&region);
+ EXPECT_TRUE(DesktopRegion(DesktopRect::MakeXYWH(1, 2, 6, 4)).Equals(region));
+}
+
+TEST_F(ScreenCapturerHelperTest, InvalidateScreen) {
+ DesktopRegion region;
+ capturer_helper_.InvalidateScreen(DesktopSize(12, 34));
+ capturer_helper_.TakeInvalidRegion(&region);
+ EXPECT_TRUE(DesktopRegion(DesktopRect::MakeWH(12, 34)).Equals(region));
+}
+
+TEST_F(ScreenCapturerHelperTest, SizeMostRecent) {
+ EXPECT_TRUE(capturer_helper_.size_most_recent().is_empty());
+ capturer_helper_.set_size_most_recent(DesktopSize(12, 34));
+ EXPECT_TRUE(DesktopSize(12, 34).equals(capturer_helper_.size_most_recent()));
+}
+
+TEST_F(ScreenCapturerHelperTest, SetLogGridSize) {
+ capturer_helper_.set_size_most_recent(DesktopSize(10, 10));
+
+ DesktopRegion region;
+ capturer_helper_.TakeInvalidRegion(&region);
+ EXPECT_TRUE(DesktopRegion().Equals(region));
+
+ capturer_helper_.InvalidateRegion(
+ DesktopRegion(DesktopRect::MakeXYWH(7, 7, 1, 1)));
+ capturer_helper_.TakeInvalidRegion(&region);
+ EXPECT_TRUE(DesktopRegion(DesktopRect::MakeXYWH(7, 7, 1, 1)).Equals(region));
+
+ capturer_helper_.SetLogGridSize(-1);
+ capturer_helper_.InvalidateRegion(
+ DesktopRegion(DesktopRect::MakeXYWH(7, 7, 1, 1)));
+ capturer_helper_.TakeInvalidRegion(&region);
+ EXPECT_TRUE(DesktopRegion(DesktopRect::MakeXYWH(7, 7, 1, 1)).Equals(region));
+
+ capturer_helper_.SetLogGridSize(0);
+ capturer_helper_.InvalidateRegion(
+ DesktopRegion(DesktopRect::MakeXYWH(7, 7, 1, 1)));
+ capturer_helper_.TakeInvalidRegion(&region);
+ EXPECT_TRUE(DesktopRegion(DesktopRect::MakeXYWH(7, 7, 1, 1)).Equals(region));
+
+ capturer_helper_.SetLogGridSize(1);
+ capturer_helper_.InvalidateRegion(
+ DesktopRegion(DesktopRect::MakeXYWH(7, 7, 1, 1)));
+ capturer_helper_.TakeInvalidRegion(&region);
+
+ EXPECT_TRUE(DesktopRegion(DesktopRect::MakeXYWH(6, 6, 2, 2)).Equals(region));
+
+ capturer_helper_.SetLogGridSize(2);
+ capturer_helper_.InvalidateRegion(
+ DesktopRegion(DesktopRect::MakeXYWH(7, 7, 1, 1)));
+ capturer_helper_.TakeInvalidRegion(&region);
+ EXPECT_TRUE(DesktopRegion(DesktopRect::MakeXYWH(4, 4, 4, 4)).Equals(region));
+
+ capturer_helper_.SetLogGridSize(0);
+ capturer_helper_.InvalidateRegion(
+ DesktopRegion(DesktopRect::MakeXYWH(7, 7, 1, 1)));
+ capturer_helper_.TakeInvalidRegion(&region);
+ EXPECT_TRUE(DesktopRegion(DesktopRect::MakeXYWH(7, 7, 1, 1)).Equals(region));
+}
+
+void TestExpandRegionToGrid(const DesktopRegion& region,
+ int log_grid_size,
+ const DesktopRegion& expanded_region_expected) {
+ DesktopRegion expanded_region1;
+ ScreenCapturerHelper::ExpandToGrid(region, log_grid_size, &expanded_region1);
+ EXPECT_TRUE(expanded_region_expected.Equals(expanded_region1));
+
+ DesktopRegion expanded_region2;
+ ScreenCapturerHelper::ExpandToGrid(expanded_region1, log_grid_size,
+ &expanded_region2);
+ EXPECT_TRUE(expanded_region1.Equals(expanded_region2));
+}
+
+void TestExpandRectToGrid(int l,
+ int t,
+ int r,
+ int b,
+ int log_grid_size,
+ int lExpanded,
+ int tExpanded,
+ int rExpanded,
+ int bExpanded) {
+ TestExpandRegionToGrid(DesktopRegion(DesktopRect::MakeLTRB(l, t, r, b)),
+ log_grid_size,
+ DesktopRegion(DesktopRect::MakeLTRB(
+ lExpanded, tExpanded, rExpanded, bExpanded)));
+}
+
+TEST_F(ScreenCapturerHelperTest, ExpandToGrid) {
+ const int kLogGridSize = 4;
+ const int kGridSize = 1 << kLogGridSize;
+ for (int i = -2; i <= 2; i++) {
+ int x = i * kGridSize;
+ for (int j = -2; j <= 2; j++) {
+ int y = j * kGridSize;
+ TestExpandRectToGrid(x + 0, y + 0, x + 1, y + 1, kLogGridSize, x + 0,
+ y + 0, x + kGridSize, y + kGridSize);
+ TestExpandRectToGrid(x + 0, y + kGridSize - 1, x + 1, y + kGridSize,
+ kLogGridSize, x + 0, y + 0, x + kGridSize,
+ y + kGridSize);
+ TestExpandRectToGrid(x + kGridSize - 1, y + kGridSize - 1, x + kGridSize,
+ y + kGridSize, kLogGridSize, x + 0, y + 0,
+ x + kGridSize, y + kGridSize);
+ TestExpandRectToGrid(x + kGridSize - 1, y + 0, x + kGridSize, y + 1,
+ kLogGridSize, x + 0, y + 0, x + kGridSize,
+ y + kGridSize);
+ TestExpandRectToGrid(x - 1, y + 0, x + 1, y + 1, kLogGridSize,
+ x - kGridSize, y + 0, x + kGridSize, y + kGridSize);
+ TestExpandRectToGrid(x - 1, y - 1, x + 1, y + 0, kLogGridSize,
+ x - kGridSize, y - kGridSize, x + kGridSize, y);
+ TestExpandRectToGrid(x + 0, y - 1, x + 1, y + 1, kLogGridSize, x,
+ y - kGridSize, x + kGridSize, y + kGridSize);
+ TestExpandRectToGrid(x - 1, y - 1, x + 0, y + 1, kLogGridSize,
+ x - kGridSize, y - kGridSize, x, y + kGridSize);
+
+ // Construct a region consisting of 3 pixels and verify that it's expanded
+ // properly to 3 squares that are kGridSize by kGridSize.
+ for (int q = 0; q < 4; ++q) {
+ DesktopRegion region;
+ DesktopRegion expanded_region_expected;
+
+ if (q != 0) {
+ region.AddRect(DesktopRect::MakeXYWH(x - 1, y - 1, 1, 1));
+ expanded_region_expected.AddRect(DesktopRect::MakeXYWH(
+ x - kGridSize, y - kGridSize, kGridSize, kGridSize));
+ }
+ if (q != 1) {
+ region.AddRect(DesktopRect::MakeXYWH(x, y - 1, 1, 1));
+ expanded_region_expected.AddRect(
+ DesktopRect::MakeXYWH(x, y - kGridSize, kGridSize, kGridSize));
+ }
+ if (q != 2) {
+ region.AddRect(DesktopRect::MakeXYWH(x - 1, y, 1, 1));
+ expanded_region_expected.AddRect(
+ DesktopRect::MakeXYWH(x - kGridSize, y, kGridSize, kGridSize));
+ }
+ if (q != 3) {
+ region.AddRect(DesktopRect::MakeXYWH(x, y, 1, 1));
+ expanded_region_expected.AddRect(
+ DesktopRect::MakeXYWH(x, y, kGridSize, kGridSize));
+ }
+
+ TestExpandRegionToGrid(region, kLogGridSize, expanded_region_expected);
+ }
+ }
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_integration_test.cc b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_integration_test.cc
new file mode 100644
index 0000000000..b33427ad42
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_integration_test.cc
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <string.h>
+
+#include <algorithm>
+#include <initializer_list>
+#include <iostream> // TODO(zijiehe): Remove once flaky has been resolved.
+#include <memory>
+#include <utility>
+
+// TODO(zijiehe): Remove once flaky has been resolved.
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/mock_desktop_capturer_callback.h"
+#include "modules/desktop_capture/rgba_color.h"
+#include "modules/desktop_capture/screen_drawer.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/third_party/base64/base64.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+#if defined(WEBRTC_WIN)
+#include "modules/desktop_capture/win/screen_capturer_win_directx.h"
+#include "rtc_base/win/windows_version.h"
+#endif // defined(WEBRTC_WIN)
+
+using ::testing::_;
+
+namespace webrtc {
+
+namespace {
+
+ACTION_P2(SaveCaptureResult, result, dest) {
+ *result = arg0;
+ *dest = std::move(*arg1);
+}
+
+// Returns true if color in `rect` of `frame` is `color`.
+bool ArePixelsColoredBy(const DesktopFrame& frame,
+ DesktopRect rect,
+ RgbaColor color,
+ bool may_partially_draw) {
+ if (!may_partially_draw) {
+ // updated_region() should cover the painted area.
+ DesktopRegion updated_region(frame.updated_region());
+ updated_region.IntersectWith(rect);
+ if (!updated_region.Equals(DesktopRegion(rect))) {
+ return false;
+ }
+ }
+
+ // Color in the `rect` should be `color`.
+ uint8_t* row = frame.GetFrameDataAtPos(rect.top_left());
+ for (int i = 0; i < rect.height(); i++) {
+ uint8_t* column = row;
+ for (int j = 0; j < rect.width(); j++) {
+ if (color != RgbaColor(column)) {
+ return false;
+ }
+ column += DesktopFrame::kBytesPerPixel;
+ }
+ row += frame.stride();
+ }
+ return true;
+}
+
+} // namespace
+
+class ScreenCapturerIntegrationTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ capturer_ = DesktopCapturer::CreateScreenCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ }
+
+ protected:
+ void TestCaptureUpdatedRegion(
+ std::initializer_list<DesktopCapturer*> capturers) {
+ RTC_DCHECK(capturers.size() > 0);
+// A large enough area for the tests, which should be able to be fulfilled
+// by most systems.
+#if defined(WEBRTC_WIN)
+ // On Windows, an interesting warning window may pop up randomly. The root
+ // cause is still under investigation, so reduce the test area to work
+ // around. Bug https://bugs.chromium.org/p/webrtc/issues/detail?id=6666.
+ const int kTestArea = 416;
+#else
+ const int kTestArea = 512;
+#endif
+ const int kRectSize = 32;
+ std::unique_ptr<ScreenDrawer> drawer = ScreenDrawer::Create();
+ if (!drawer || drawer->DrawableRegion().is_empty()) {
+ RTC_LOG(LS_WARNING)
+ << "No ScreenDrawer implementation for current platform.";
+ return;
+ }
+ if (drawer->DrawableRegion().width() < kTestArea ||
+ drawer->DrawableRegion().height() < kTestArea) {
+ RTC_LOG(LS_WARNING)
+ << "ScreenDrawer::DrawableRegion() is too small for the "
+ "CaptureUpdatedRegion tests.";
+ return;
+ }
+
+ for (DesktopCapturer* capturer : capturers) {
+ capturer->Start(&callback_);
+ }
+
+ // Draw a set of `kRectSize` by `kRectSize` rectangles at (`i`, `i`), or
+ // `i` by `i` rectangles at (`kRectSize`, `kRectSize`). One of (controlled
+ // by `c`) its primary colors is `i`, and the other two are 0x7f. So we
+ // won't draw a black or white rectangle.
+ for (int c = 0; c < 3; c++) {
+ // A fixed size rectangle.
+ for (int i = 0; i < kTestArea - kRectSize; i += 16) {
+ DesktopRect rect = DesktopRect::MakeXYWH(i, i, kRectSize, kRectSize);
+ rect.Translate(drawer->DrawableRegion().top_left());
+ RgbaColor color((c == 0 ? (i & 0xff) : 0x7f),
+ (c == 1 ? (i & 0xff) : 0x7f),
+ (c == 2 ? (i & 0xff) : 0x7f));
+ // Fail fast.
+ ASSERT_NO_FATAL_FAILURE(
+ TestCaptureOneFrame(capturers, drawer.get(), rect, color));
+ }
+
+ // A variable-size rectangle.
+ for (int i = 0; i < kTestArea - kRectSize; i += 16) {
+ DesktopRect rect = DesktopRect::MakeXYWH(kRectSize, kRectSize, i, i);
+ rect.Translate(drawer->DrawableRegion().top_left());
+ RgbaColor color((c == 0 ? (i & 0xff) : 0x7f),
+ (c == 1 ? (i & 0xff) : 0x7f),
+ (c == 2 ? (i & 0xff) : 0x7f));
+ // Fail fast.
+ ASSERT_NO_FATAL_FAILURE(
+ TestCaptureOneFrame(capturers, drawer.get(), rect, color));
+ }
+ }
+ }
+
+ void TestCaptureUpdatedRegion() {
+ TestCaptureUpdatedRegion({capturer_.get()});
+ }
+
+#if defined(WEBRTC_WIN)
+ // Enable allow_directx_capturer in DesktopCaptureOptions, but let
+ // DesktopCapturer::CreateScreenCapturer() to decide whether a DirectX
+ // capturer should be used.
+ void MaybeCreateDirectxCapturer() {
+ DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault());
+ options.set_allow_directx_capturer(true);
+ capturer_ = DesktopCapturer::CreateScreenCapturer(options);
+ }
+
+ bool CreateDirectxCapturer() {
+ if (!ScreenCapturerWinDirectx::IsSupported()) {
+ RTC_LOG(LS_WARNING) << "Directx capturer is not supported";
+ return false;
+ }
+
+ MaybeCreateDirectxCapturer();
+ return true;
+ }
+
+ void CreateMagnifierCapturer() {
+ DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault());
+ options.set_allow_use_magnification_api(true);
+ capturer_ = DesktopCapturer::CreateScreenCapturer(options);
+ }
+#endif // defined(WEBRTC_WIN)
+
+ std::unique_ptr<DesktopCapturer> capturer_;
+ MockDesktopCapturerCallback callback_;
+
+ private:
+ // Repeats capturing the frame by using `capturers` one-by-one for 600 times,
+ // typically 30 seconds, until they succeeded captured a `color` rectangle at
+ // `rect`. This function uses `drawer`->WaitForPendingDraws() between two
+ // attempts to wait for the screen to update.
+ void TestCaptureOneFrame(std::vector<DesktopCapturer*> capturers,
+ ScreenDrawer* drawer,
+ DesktopRect rect,
+ RgbaColor color) {
+ const int wait_capture_round = 600;
+ drawer->Clear();
+ size_t succeeded_capturers = 0;
+ for (int i = 0; i < wait_capture_round; i++) {
+ drawer->DrawRectangle(rect, color);
+ drawer->WaitForPendingDraws();
+ for (size_t j = 0; j < capturers.size(); j++) {
+ if (capturers[j] == nullptr) {
+ // DesktopCapturer should return an empty updated_region() if no
+ // update detected. So we won't test it again if it has captured the
+ // rectangle we drew.
+ continue;
+ }
+ std::unique_ptr<DesktopFrame> frame = CaptureFrame(capturers[j]);
+ if (!frame) {
+ // CaptureFrame() has triggered an assertion failure already, we only
+ // need to return here.
+ return;
+ }
+
+ if (ArePixelsColoredBy(*frame, rect, color,
+ drawer->MayDrawIncompleteShapes())) {
+ capturers[j] = nullptr;
+ succeeded_capturers++;
+ }
+ // The following else if statement is for debugging purpose only, which
+ // should be removed after flaky of ScreenCapturerIntegrationTest has
+ // been resolved.
+ else if (i == wait_capture_round - 1) {
+ std::string result;
+ rtc::Base64::EncodeFromArray(
+ frame->data(), frame->size().height() * frame->stride(), &result);
+ std::cout << frame->size().width() << " x " << frame->size().height()
+ << std::endl;
+ // Split the entire string (can be over 4M) into several lines to
+ // avoid browser from sticking.
+ static const size_t kLineLength = 32768;
+ const char* result_end = result.c_str() + result.length();
+ for (const char* it = result.c_str(); it < result_end;
+ it += kLineLength) {
+ const size_t max_length = result_end - it;
+ std::cout << std::string(it, std::min(kLineLength, max_length))
+ << std::endl;
+ }
+ std::cout << "Failed to capture rectangle " << rect.left() << " x "
+ << rect.top() << " - " << rect.right() << " x "
+ << rect.bottom() << " with color ("
+ << static_cast<int>(color.red) << ", "
+ << static_cast<int>(color.green) << ", "
+ << static_cast<int>(color.blue) << ", "
+ << static_cast<int>(color.alpha) << ")" << std::endl;
+ ASSERT_TRUE(false) << "ScreenCapturerIntegrationTest may be flaky. "
+ "Please kindly FYI the broken link to "
+ "zijiehe@chromium.org for investigation. If "
+ "the failure continually happens, but I have "
+ "not responded as quick as expected, disable "
+ "*all* tests in "
+ "screen_capturer_integration_test.cc to "
+ "unblock other developers.";
+ }
+ }
+
+ if (succeeded_capturers == capturers.size()) {
+ break;
+ }
+ }
+
+ ASSERT_EQ(succeeded_capturers, capturers.size());
+ }
+
+ // Expects `capturer` to successfully capture a frame, and returns it.
+ std::unique_ptr<DesktopFrame> CaptureFrame(DesktopCapturer* capturer) {
+ for (int i = 0; i < 10; i++) {
+ std::unique_ptr<DesktopFrame> frame;
+ DesktopCapturer::Result result;
+ EXPECT_CALL(callback_, OnCaptureResultPtr(_, _))
+ .WillOnce(SaveCaptureResult(&result, &frame));
+ capturer->CaptureFrame();
+ ::testing::Mock::VerifyAndClearExpectations(&callback_);
+ if (result == DesktopCapturer::Result::SUCCESS) {
+ EXPECT_TRUE(frame);
+ return frame;
+ } else {
+ EXPECT_FALSE(frame);
+ }
+ }
+
+ EXPECT_TRUE(false);
+ return nullptr;
+ }
+};
+
+#if defined(WEBRTC_WIN)
+// ScreenCapturerWinGdi randomly returns blank screen, the root cause is still
+// unknown. Bug, https://bugs.chromium.org/p/webrtc/issues/detail?id=6843.
+#define MAYBE_CaptureUpdatedRegion DISABLED_CaptureUpdatedRegion
+#else
+#define MAYBE_CaptureUpdatedRegion CaptureUpdatedRegion
+#endif
+TEST_F(ScreenCapturerIntegrationTest, MAYBE_CaptureUpdatedRegion) {
+ TestCaptureUpdatedRegion();
+}
+
+#if defined(WEBRTC_WIN)
+// ScreenCapturerWinGdi randomly returns blank screen, the root cause is still
+// unknown. Bug, https://bugs.chromium.org/p/webrtc/issues/detail?id=6843.
+#define MAYBE_TwoCapturers DISABLED_TwoCapturers
+#else
+#define MAYBE_TwoCapturers TwoCapturers
+#endif
+TEST_F(ScreenCapturerIntegrationTest, MAYBE_TwoCapturers) {
+ std::unique_ptr<DesktopCapturer> capturer2 = std::move(capturer_);
+ SetUp();
+ TestCaptureUpdatedRegion({capturer_.get(), capturer2.get()});
+}
+
+#if defined(WEBRTC_WIN)
+
+// Windows cannot capture contents on VMs hosted in GCE. See bug
+// https://bugs.chromium.org/p/webrtc/issues/detail?id=8153.
+TEST_F(ScreenCapturerIntegrationTest,
+ DISABLED_CaptureUpdatedRegionWithDirectxCapturer) {
+ if (!CreateDirectxCapturer()) {
+ return;
+ }
+
+ TestCaptureUpdatedRegion();
+}
+
+TEST_F(ScreenCapturerIntegrationTest, DISABLED_TwoDirectxCapturers) {
+ if (!CreateDirectxCapturer()) {
+ return;
+ }
+
+ std::unique_ptr<DesktopCapturer> capturer2 = std::move(capturer_);
+ RTC_CHECK(CreateDirectxCapturer());
+ TestCaptureUpdatedRegion({capturer_.get(), capturer2.get()});
+}
+
+TEST_F(ScreenCapturerIntegrationTest,
+ DISABLED_CaptureUpdatedRegionWithMagnifierCapturer) {
+ // On Windows 8 or later, magnifier APIs return a frame with a border on test
+ // environment, so disable these tests.
+ // Bug https://bugs.chromium.org/p/webrtc/issues/detail?id=6844
+ // TODO(zijiehe): Find the root cause of the border and failure, which cannot
+ // reproduce on my dev machine.
+ if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN8) {
+ return;
+ }
+ CreateMagnifierCapturer();
+ TestCaptureUpdatedRegion();
+}
+
+TEST_F(ScreenCapturerIntegrationTest, DISABLED_TwoMagnifierCapturers) {
+ // On Windows 8 or later, magnifier APIs return a frame with a border on test
+ // environment, so disable these tests.
+ // Bug https://bugs.chromium.org/p/webrtc/issues/detail?id=6844
+ // TODO(zijiehe): Find the root cause of the border and failure, which cannot
+ // reproduce on my dev machine.
+ if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN8) {
+ return;
+ }
+ CreateMagnifierCapturer();
+ std::unique_ptr<DesktopCapturer> capturer2 = std::move(capturer_);
+ CreateMagnifierCapturer();
+ TestCaptureUpdatedRegion({capturer_.get(), capturer2.get()});
+}
+
+TEST_F(ScreenCapturerIntegrationTest,
+ DISABLED_MaybeCaptureUpdatedRegionWithDirectxCapturer) {
+ if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN8) {
+ // ScreenCapturerWinGdi randomly returns blank screen, the root cause is
+ // still unknown. Bug,
+ // https://bugs.chromium.org/p/webrtc/issues/detail?id=6843.
+ // On Windows 7 or early version, MaybeCreateDirectxCapturer() always
+ // creates GDI capturer.
+ return;
+ }
+ // Even DirectX capturer is not supported in current system, we should be able
+ // to select a usable capturer.
+ MaybeCreateDirectxCapturer();
+ TestCaptureUpdatedRegion();
+}
+
+#endif // defined(WEBRTC_WIN)
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_linux.cc b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_linux.cc
new file mode 100644
index 0000000000..d9f2795130
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_linux.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+
+#if defined(WEBRTC_USE_PIPEWIRE)
+#if defined(WEBRTC_MOZILLA_BUILD)
+#include "modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.h"
+#else
+#include "modules/desktop_capture/linux/wayland/base_capturer_pipewire.h"
+#endif
+#endif // defined(WEBRTC_USE_PIPEWIRE)
+
+#if defined(WEBRTC_USE_X11)
+#include "modules/desktop_capture/linux/x11/screen_capturer_x11.h"
+#endif // defined(WEBRTC_USE_X11)
+
+namespace webrtc {
+
+// static
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options) {
+#if defined(WEBRTC_USE_PIPEWIRE)
+ if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) {
+#if defined(WEBRTC_MOZILLA_BUILD)
+ return BaseCapturerPipeWire::CreateRawCapturer(options);
+#else
+ return std::make_unique<BaseCapturerPipeWire>(options);
+#endif
+ }
+#endif // defined(WEBRTC_USE_PIPEWIRE)
+
+#if defined(WEBRTC_USE_X11)
+ return ScreenCapturerX11::CreateRawScreenCapturer(options);
+#else
+ return nullptr;
+#endif // defined(WEBRTC_USE_X11)
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_mac.mm b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_mac.mm
new file mode 100644
index 0000000000..285086ffa6
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_mac.mm
@@ -0,0 +1,766 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <stddef.h>
+
+#include <memory>
+#include <set>
+#include <utility>
+
+#include <ApplicationServices/ApplicationServices.h>
+#include <Cocoa/Cocoa.h>
+#include <CoreGraphics/CoreGraphics.h>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/mac/desktop_configuration.h"
+#include "modules/desktop_capture/mac/desktop_configuration_monitor.h"
+#include "modules/desktop_capture/mac/scoped_pixel_buffer_object.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/screen_capturer_helper.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/constructormagic.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/macutils.h"
+#include "rtc_base/timeutils.h"
+
+namespace webrtc {
+
+namespace {
+
+// CGDisplayStreamRefs need to be destroyed asynchronously after receiving a
+// kCGDisplayStreamFrameStatusStopped callback from CoreGraphics. This may
+// happen after the ScreenCapturerMac has been destroyed. DisplayStreamManager
+// is responsible for destroying all extant CGDisplayStreamRefs, and will
+// destroy itself once it's done.
+class DisplayStreamManager {
+ public:
+ int GetUniqueId() { return ++unique_id_generator_; }
+ void DestroyStream(int unique_id) {
+ auto it = display_stream_wrappers_.find(unique_id);
+ RTC_CHECK(it != display_stream_wrappers_.end());
+ RTC_CHECK(!it->second.active);
+ CFRelease(it->second.stream);
+ display_stream_wrappers_.erase(it);
+
+ if (ready_for_self_destruction_ && display_stream_wrappers_.empty())
+ delete this;
+ }
+
+ void SaveStream(int unique_id,
+ CGDisplayStreamRef stream) {
+ RTC_CHECK(unique_id <= unique_id_generator_);
+ DisplayStreamWrapper wrapper;
+ wrapper.stream = stream;
+ display_stream_wrappers_[unique_id] = wrapper;
+ }
+
+ void UnregisterActiveStreams() {
+ for (auto& pair : display_stream_wrappers_) {
+ DisplayStreamWrapper& wrapper = pair.second;
+ if (wrapper.active) {
+ wrapper.active = false;
+ CFRunLoopSourceRef source =
+ CGDisplayStreamGetRunLoopSource(wrapper.stream);
+ CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source,
+ kCFRunLoopCommonModes);
+ CGDisplayStreamStop(wrapper.stream);
+ }
+ }
+ }
+
+ void PrepareForSelfDestruction() {
+ ready_for_self_destruction_ = true;
+
+ if (display_stream_wrappers_.empty())
+ delete this;
+ }
+
+ // Once the DisplayStreamManager is ready for destruction, the
+ // ScreenCapturerMac is no longer present. Any updates should be ignored.
+ bool ShouldIgnoreUpdates() { return ready_for_self_destruction_; }
+
+ private:
+ struct DisplayStreamWrapper {
+ // The registered CGDisplayStreamRef.
+ CGDisplayStreamRef stream = nullptr;
+
+ // Set to false when the stream has been stopped. An asynchronous callback
+ // from CoreGraphics will let us destroy the CGDisplayStreamRef.
+ bool active = true;
+ };
+
+ std::map<int, DisplayStreamWrapper> display_stream_wrappers_;
+ int unique_id_generator_ = 0;
+ bool ready_for_self_destruction_ = false;
+};
+
+// Standard Mac displays have 72dpi, but we report 96dpi for
+// consistency with Windows and Linux.
+const int kStandardDPI = 96;
+
+// Scales all coordinates of a rect by a specified factor.
+DesktopRect ScaleAndRoundCGRect(const CGRect& rect, float scale) {
+ return DesktopRect::MakeLTRB(
+ static_cast<int>(floor(rect.origin.x * scale)),
+ static_cast<int>(floor(rect.origin.y * scale)),
+ static_cast<int>(ceil((rect.origin.x + rect.size.width) * scale)),
+ static_cast<int>(ceil((rect.origin.y + rect.size.height) * scale)));
+}
+
+// Copy pixels in the |rect| from |src_place| to |dest_plane|. |rect| should be
+// relative to the origin of |src_plane| and |dest_plane|.
+void CopyRect(const uint8_t* src_plane,
+ int src_plane_stride,
+ uint8_t* dest_plane,
+ int dest_plane_stride,
+ int bytes_per_pixel,
+ const DesktopRect& rect) {
+ // Get the address of the starting point.
+ const int src_y_offset = src_plane_stride * rect.top();
+ const int dest_y_offset = dest_plane_stride * rect.top();
+ const int x_offset = bytes_per_pixel * rect.left();
+ src_plane += src_y_offset + x_offset;
+ dest_plane += dest_y_offset + x_offset;
+
+ // Copy pixels in the rectangle line by line.
+ const int bytes_per_line = bytes_per_pixel * rect.width();
+ const int height = rect.height();
+ for (int i = 0 ; i < height; ++i) {
+ memcpy(dest_plane, src_plane, bytes_per_line);
+ src_plane += src_plane_stride;
+ dest_plane += dest_plane_stride;
+ }
+}
+
+// Returns an array of CGWindowID for all the on-screen windows except
+// |window_to_exclude|, or NULL if the window is not found or it fails. The
+// caller should release the returned CFArrayRef.
+CFArrayRef CreateWindowListWithExclusion(CGWindowID window_to_exclude) {
+ if (!window_to_exclude)
+ return nullptr;
+
+ CFArrayRef all_windows = CGWindowListCopyWindowInfo(
+ kCGWindowListOptionOnScreenOnly, kCGNullWindowID);
+ if (!all_windows)
+ return nullptr;
+
+ CFMutableArrayRef returned_array =
+ CFArrayCreateMutable(nullptr, CFArrayGetCount(all_windows), nullptr);
+
+ bool found = false;
+ for (CFIndex i = 0; i < CFArrayGetCount(all_windows); ++i) {
+ CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
+ CFArrayGetValueAtIndex(all_windows, i));
+
+ CFNumberRef id_ref = reinterpret_cast<CFNumberRef>(
+ CFDictionaryGetValue(window, kCGWindowNumber));
+
+ CGWindowID id;
+ CFNumberGetValue(id_ref, kCFNumberIntType, &id);
+ if (id == window_to_exclude) {
+ found = true;
+ continue;
+ }
+ CFArrayAppendValue(returned_array, reinterpret_cast<void *>(id));
+ }
+ CFRelease(all_windows);
+
+ if (!found) {
+ CFRelease(returned_array);
+ returned_array = nullptr;
+ }
+ return returned_array;
+}
+
+// Returns the bounds of |window| in physical pixels, enlarged by a small amount
+// on four edges to take account of the border/shadow effects.
+DesktopRect GetExcludedWindowPixelBounds(CGWindowID window,
+ float dip_to_pixel_scale) {
+ // The amount of pixels to add to the actual window bounds to take into
+ // account of the border/shadow effects.
+ static const int kBorderEffectSize = 20;
+ CGRect rect;
+ CGWindowID ids[1];
+ ids[0] = window;
+
+ CFArrayRef window_id_array =
+ CFArrayCreate(nullptr, reinterpret_cast<const void**>(&ids), 1, nullptr);
+ CFArrayRef window_array =
+ CGWindowListCreateDescriptionFromArray(window_id_array);
+
+ if (CFArrayGetCount(window_array) > 0) {
+ CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
+ CFArrayGetValueAtIndex(window_array, 0));
+ CFDictionaryRef bounds_ref = reinterpret_cast<CFDictionaryRef>(
+ CFDictionaryGetValue(window, kCGWindowBounds));
+ CGRectMakeWithDictionaryRepresentation(bounds_ref, &rect);
+ }
+
+ CFRelease(window_id_array);
+ CFRelease(window_array);
+
+ rect.origin.x -= kBorderEffectSize;
+ rect.origin.y -= kBorderEffectSize;
+ rect.size.width += kBorderEffectSize * 2;
+ rect.size.height += kBorderEffectSize * 2;
+ // |rect| is in DIP, so convert to physical pixels.
+ return ScaleAndRoundCGRect(rect, dip_to_pixel_scale);
+}
+
+// Create an image of the given region using the given |window_list|.
+// |pixel_bounds| should be in the primary display's coordinate in physical
+// pixels. The caller should release the returned CGImageRef and CFDataRef.
+CGImageRef CreateExcludedWindowRegionImage(const DesktopRect& pixel_bounds,
+ float dip_to_pixel_scale,
+ CFArrayRef window_list) {
+ CGRect window_bounds;
+ // The origin is in DIP while the size is in physical pixels. That's what
+ // CGWindowListCreateImageFromArray expects.
+ window_bounds.origin.x = pixel_bounds.left() / dip_to_pixel_scale;
+ window_bounds.origin.y = pixel_bounds.top() / dip_to_pixel_scale;
+ window_bounds.size.width = pixel_bounds.width();
+ window_bounds.size.height = pixel_bounds.height();
+
+ return CGWindowListCreateImageFromArray(
+ window_bounds, window_list, kCGWindowImageDefault);
+}
+
+// A class to perform video frame capturing for mac.
+class ScreenCapturerMac : public DesktopCapturer {
+ public:
+ explicit ScreenCapturerMac(
+ rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor,
+ bool detect_updated_region);
+ ~ScreenCapturerMac() override;
+
+ bool Init();
+
+ // DesktopCapturer interface.
+ void Start(Callback* callback) override;
+ void CaptureFrame() override;
+ void SetExcludedWindow(WindowId window) override;
+ bool GetSourceList(SourceList* screens) override;
+ bool SelectSource(SourceId id) override;
+
+ private:
+ // Returns false if the selected screen is no longer valid.
+ bool CgBlit(const DesktopFrame& frame, const DesktopRegion& region);
+
+ // Called when the screen configuration is changed.
+ void ScreenConfigurationChanged();
+
+ bool RegisterRefreshAndMoveHandlers();
+ void UnregisterRefreshAndMoveHandlers();
+
+ void ScreenRefresh(CGRectCount count,
+ const CGRect *rect_array,
+ DesktopVector display_origin);
+ void ReleaseBuffers();
+
+ std::unique_ptr<DesktopFrame> CreateFrame();
+
+ const bool detect_updated_region_;
+
+ Callback* callback_ = nullptr;
+
+ ScopedPixelBufferObject pixel_buffer_object_;
+
+ // Queue of the frames buffers.
+ ScreenCaptureFrameQueue<SharedDesktopFrame> queue_;
+
+ // Current display configuration.
+ MacDesktopConfiguration desktop_config_;
+
+ // Currently selected display, or 0 if the full desktop is selected. On OS X
+ // 10.6 and before, this is always 0.
+ CGDirectDisplayID current_display_ = 0;
+
+ // The physical pixel bounds of the current screen.
+ DesktopRect screen_pixel_bounds_;
+
+ // The dip to physical pixel scale of the current screen.
+ float dip_to_pixel_scale_ = 1.0f;
+
+ // A thread-safe list of invalid rectangles, and the size of the most
+ // recently captured screen.
+ ScreenCapturerHelper helper_;
+
+ // Contains an invalid region from the previous capture.
+ DesktopRegion last_invalid_region_;
+
+ // Monitoring display reconfiguration.
+ rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor_;
+
+ CGWindowID excluded_window_ = 0;
+
+ // A self-owned object that will destroy itself after ScreenCapturerMac and
+ // all display streams have been destroyed..
+ DisplayStreamManager* display_stream_manager_;
+
+ // Used to force CaptureFrame to update it's screen configuration
+ // and reregister event handlers. This ensure that this
+ // occurs on the ScreenCapture thread. Read and written from
+ // both the VideoCapture thread and ScreenCapture thread.
+ // Protected by desktop_config_monitor_.
+ bool update_screen_configuration_ = false;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerMac);
+};
+
+// DesktopFrame wrapper that flips wrapped frame upside down by inverting
+// stride.
+class InvertedDesktopFrame : public DesktopFrame {
+ public:
+ InvertedDesktopFrame(std::unique_ptr<DesktopFrame> frame)
+ : DesktopFrame(
+ frame->size(),
+ -frame->stride(),
+ frame->data() + (frame->size().height() - 1) * frame->stride(),
+ frame->shared_memory()) {
+ original_frame_ = std::move(frame);
+ MoveFrameInfoFrom(original_frame_.get());
+ }
+ ~InvertedDesktopFrame() override {}
+
+ private:
+ std::unique_ptr<DesktopFrame> original_frame_;
+
+ RTC_DISALLOW_COPY_AND_ASSIGN(InvertedDesktopFrame);
+};
+
+ScreenCapturerMac::ScreenCapturerMac(
+ rtc::scoped_refptr<DesktopConfigurationMonitor> desktop_config_monitor,
+ bool detect_updated_region)
+ : detect_updated_region_(detect_updated_region),
+ desktop_config_monitor_(desktop_config_monitor) {
+ display_stream_manager_ = new DisplayStreamManager;
+}
+
+ScreenCapturerMac::~ScreenCapturerMac() {
+ ReleaseBuffers();
+ UnregisterRefreshAndMoveHandlers();
+ display_stream_manager_->PrepareForSelfDestruction();
+}
+
+bool ScreenCapturerMac::Init() {
+ desktop_config_monitor_->Lock();
+ desktop_config_ = desktop_config_monitor_->desktop_configuration();
+ desktop_config_monitor_->Unlock();
+ if (!RegisterRefreshAndMoveHandlers()) {
+ return false;
+ }
+ ScreenConfigurationChanged();
+ return true;
+}
+
+void ScreenCapturerMac::ReleaseBuffers() {
+ // The buffers might be in use by the encoder, so don't delete them here.
+ // Instead, mark them as "needs update"; next time the buffers are used by
+ // the capturer, they will be recreated if necessary.
+ queue_.Reset();
+}
+
+void ScreenCapturerMac::Start(Callback* callback) {
+ assert(!callback_);
+ assert(callback);
+
+ callback_ = callback;
+ desktop_config_monitor_->Lock();
+ update_screen_configuration_ = true;
+ desktop_config_monitor_->Unlock();
+}
+
+void ScreenCapturerMac::CaptureFrame() {
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ // Spin RunLoop for 1/100th of a second, handling at most one source
+ CFRunLoopRunInMode(kCFRunLoopDefaultMode, 0.01, true);
+
+ queue_.MoveToNextFrame();
+ RTC_DCHECK(!queue_.current_frame() || !queue_.current_frame()->IsShared());
+
+ desktop_config_monitor_->Lock();
+ MacDesktopConfiguration new_config =
+ desktop_config_monitor_->desktop_configuration();
+ if (update_screen_configuration_ || !desktop_config_.Equals(new_config)) {
+ update_screen_configuration_ = false;
+ desktop_config_ = new_config;
+ // If the display configuraiton has changed then refresh capturer data
+ // structures. Occasionally, the refresh and move handlers are lost when
+ // the screen mode changes, so re-register them here.
+ UnregisterRefreshAndMoveHandlers();
+ RegisterRefreshAndMoveHandlers();
+ ScreenConfigurationChanged();
+ }
+
+ DesktopRegion region;
+ helper_.TakeInvalidRegion(&region);
+
+ // If the current buffer is from an older generation then allocate a new one.
+ // Note that we can't reallocate other buffers at this point, since the caller
+ // may still be reading from them.
+ if (!queue_.current_frame())
+ queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(CreateFrame()));
+
+ DesktopFrame* current_frame = queue_.current_frame();
+
+ if (!CgBlit(*current_frame, region)) {
+ desktop_config_monitor_->Unlock();
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+ std::unique_ptr<DesktopFrame> new_frame = queue_.current_frame()->Share();
+ if (detect_updated_region_) {
+ *new_frame->mutable_updated_region() = region;
+ } else {
+ new_frame->mutable_updated_region()->AddRect(
+ DesktopRect::MakeSize(new_frame->size()));
+ }
+
+ if (current_display_) {
+ const MacDisplayConfiguration* config =
+ desktop_config_.FindDisplayConfigurationById(current_display_);
+ if (config) {
+ new_frame->set_top_left(config->bounds.top_left().subtract(
+ desktop_config_.bounds.top_left()));
+ }
+ }
+
+ helper_.set_size_most_recent(new_frame->size());
+
+ // Signal that we are done capturing data from the display framebuffer,
+ // and accessing display structures.
+ desktop_config_monitor_->Unlock();
+
+ new_frame->set_capture_time_ms((rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec);
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(new_frame));
+}
+
+void ScreenCapturerMac::SetExcludedWindow(WindowId window) {
+ excluded_window_ = window;
+}
+
+bool ScreenCapturerMac::GetSourceList(SourceList* screens) {
+ assert(screens->size() == 0);
+
+ for (MacDisplayConfigurations::iterator it = desktop_config_.displays.begin();
+ it != desktop_config_.displays.end(); ++it) {
+ screens->push_back({it->id});
+ }
+ return true;
+}
+
+bool ScreenCapturerMac::SelectSource(SourceId id) {
+ if (id == kFullDesktopScreenId) {
+ current_display_ = 0;
+ } else {
+ const MacDisplayConfiguration* config =
+ desktop_config_.FindDisplayConfigurationById(
+ static_cast<CGDirectDisplayID>(id));
+ if (!config)
+ return false;
+ current_display_ = config->id;
+ }
+
+ ScreenConfigurationChanged();
+ return true;
+}
+
+bool ScreenCapturerMac::CgBlit(const DesktopFrame& frame, const DesktopRegion& region) {
+ // Copy the entire contents of the previous capture buffer, to capture over.
+ // TODO(wez): Get rid of this as per crbug.com/145064, or implement
+ // crbug.com/92354.
+ if (queue_.previous_frame()) {
+ memcpy(frame.data(), queue_.previous_frame()->data(),
+ frame.stride() * frame.size().height());
+ }
+
+ MacDisplayConfigurations displays_to_capture;
+ if (current_display_) {
+ // Capturing a single screen. Note that the screen id may change when
+ // screens are added or removed.
+ const MacDisplayConfiguration* config =
+ desktop_config_.FindDisplayConfigurationById(current_display_);
+ if (config) {
+ displays_to_capture.push_back(*config);
+ } else {
+ RTC_LOG(LS_ERROR) << "The selected screen cannot be found for capturing.";
+ return false;
+ }
+ } else {
+ // Capturing the whole desktop.
+ displays_to_capture = desktop_config_.displays;
+ }
+
+ // Create the window list once for all displays.
+ CFArrayRef window_list = CreateWindowListWithExclusion(excluded_window_);
+
+ for (size_t i = 0; i < displays_to_capture.size(); ++i) {
+ const MacDisplayConfiguration& display_config = displays_to_capture[i];
+
+ // Capturing mixed-DPI on one surface is hard, so we only return displays
+ // that match the "primary" display's DPI. The primary display is always
+ // the first in the list.
+ if (i > 0 && display_config.dip_to_pixel_scale !=
+ displays_to_capture[0].dip_to_pixel_scale) {
+ continue;
+ }
+ // Determine the display's position relative to the desktop, in pixels.
+ DesktopRect display_bounds = display_config.pixel_bounds;
+ display_bounds.Translate(-screen_pixel_bounds_.left(),
+ -screen_pixel_bounds_.top());
+
+ // Determine which parts of the blit region, if any, lay within the monitor.
+ DesktopRegion copy_region = region;
+ copy_region.IntersectWith(display_bounds);
+ if (copy_region.is_empty())
+ continue;
+
+ // Translate the region to be copied into display-relative coordinates.
+ copy_region.Translate(-display_bounds.left(), -display_bounds.top());
+
+ DesktopRect excluded_window_bounds;
+ CGImageRef excluded_image = nullptr;
+ if (excluded_window_ && window_list) {
+ // Get the region of the excluded window relative the primary display.
+ excluded_window_bounds = GetExcludedWindowPixelBounds(
+ excluded_window_, display_config.dip_to_pixel_scale);
+ excluded_window_bounds.IntersectWith(display_config.pixel_bounds);
+
+ // Create the image under the excluded window first, because it's faster
+ // than captuing the whole display.
+ if (!excluded_window_bounds.is_empty()) {
+ excluded_image = CreateExcludedWindowRegionImage(
+ excluded_window_bounds, display_config.dip_to_pixel_scale,
+ window_list);
+ }
+ }
+
+ // Create an image containing a snapshot of the display.
+ CGImageRef image = CGDisplayCreateImage(display_config.id);
+ if (!image) {
+ if (excluded_image)
+ CFRelease(excluded_image);
+ continue;
+ }
+
+ // Verify that the image has 32-bit depth.
+ int bits_per_pixel = CGImageGetBitsPerPixel(image);
+ if (bits_per_pixel / 8 != DesktopFrame::kBytesPerPixel) {
+ RTC_LOG(LS_ERROR) << "CGDisplayCreateImage() returned imaged with " << bits_per_pixel
+ << " bits per pixel. Only 32-bit depth is supported.";
+ CFRelease(image);
+ if (excluded_image)
+ CFRelease(excluded_image);
+ return false;
+ }
+
+ // Request access to the raw pixel data via the image's DataProvider.
+ CGDataProviderRef provider = CGImageGetDataProvider(image);
+ CFDataRef data = CGDataProviderCopyData(provider);
+ assert(data);
+
+ const uint8_t* display_base_address = CFDataGetBytePtr(data);
+ int src_bytes_per_row = CGImageGetBytesPerRow(image);
+
+ // |image| size may be different from display_bounds in case the screen was
+ // resized recently.
+ copy_region.IntersectWith(
+ DesktopRect::MakeWH(CGImageGetWidth(image), CGImageGetHeight(image)));
+
+ // Copy the dirty region from the display buffer into our desktop buffer.
+ uint8_t* out_ptr = frame.GetFrameDataAtPos(display_bounds.top_left());
+ for (DesktopRegion::Iterator i(copy_region); !i.IsAtEnd(); i.Advance()) {
+ CopyRect(display_base_address, src_bytes_per_row, out_ptr, frame.stride(),
+ DesktopFrame::kBytesPerPixel, i.rect());
+ }
+
+ CFRelease(data);
+ CFRelease(image);
+
+ if (excluded_image) {
+ CGDataProviderRef provider = CGImageGetDataProvider(excluded_image);
+ CFDataRef excluded_image_data = CGDataProviderCopyData(provider);
+ assert(excluded_image_data);
+ display_base_address = CFDataGetBytePtr(excluded_image_data);
+ src_bytes_per_row = CGImageGetBytesPerRow(excluded_image);
+
+ // Translate the bounds relative to the desktop, because |frame| data
+ // starts from the desktop top-left corner.
+ DesktopRect window_bounds_relative_to_desktop(excluded_window_bounds);
+ window_bounds_relative_to_desktop.Translate(-screen_pixel_bounds_.left(),
+ -screen_pixel_bounds_.top());
+
+ DesktopRect rect_to_copy =
+ DesktopRect::MakeSize(excluded_window_bounds.size());
+ rect_to_copy.IntersectWith(DesktopRect::MakeWH(
+ CGImageGetWidth(excluded_image), CGImageGetHeight(excluded_image)));
+
+ if (CGImageGetBitsPerPixel(excluded_image) / 8 ==
+ DesktopFrame::kBytesPerPixel) {
+ CopyRect(display_base_address, src_bytes_per_row,
+ frame.GetFrameDataAtPos(
+ window_bounds_relative_to_desktop.top_left()),
+ frame.stride(), DesktopFrame::kBytesPerPixel, rect_to_copy);
+ }
+
+ CFRelease(excluded_image_data);
+ CFRelease(excluded_image);
+ }
+ }
+ if (window_list)
+ CFRelease(window_list);
+ return true;
+}
+
+void ScreenCapturerMac::ScreenConfigurationChanged() {
+ if (current_display_) {
+ const MacDisplayConfiguration* config =
+ desktop_config_.FindDisplayConfigurationById(current_display_);
+ screen_pixel_bounds_ = config ? config->pixel_bounds : DesktopRect();
+ dip_to_pixel_scale_ = config ? config->dip_to_pixel_scale : 1.0f;
+ } else {
+ screen_pixel_bounds_ = desktop_config_.pixel_bounds;
+ dip_to_pixel_scale_ = desktop_config_.dip_to_pixel_scale;
+ }
+
+ // Release existing buffers, which will be of the wrong size.
+ ReleaseBuffers();
+
+ // Clear the dirty region, in case the display is down-sizing.
+ helper_.ClearInvalidRegion();
+
+ // Re-mark the entire desktop as dirty.
+ helper_.InvalidateScreen(screen_pixel_bounds_.size());
+
+ // Make sure the frame buffers will be reallocated.
+ queue_.Reset();
+}
+
+bool ScreenCapturerMac::RegisterRefreshAndMoveHandlers() {
+ desktop_config_ = desktop_config_monitor_->desktop_configuration();
+ for (const auto& config : desktop_config_.displays) {
+ size_t pixel_width = config.pixel_bounds.width();
+ size_t pixel_height = config.pixel_bounds.height();
+ if (pixel_width == 0 || pixel_height == 0)
+ continue;
+ // Using a local variable forces the block to capture the raw pointer.
+ DisplayStreamManager* manager = display_stream_manager_;
+ int unique_id = manager->GetUniqueId();
+ CGDirectDisplayID display_id = config.id;
+ DesktopVector display_origin = config.pixel_bounds.top_left();
+
+ CGDisplayStreamFrameAvailableHandler handler =
+ ^(CGDisplayStreamFrameStatus status, uint64_t display_time,
+ IOSurfaceRef frame_surface, CGDisplayStreamUpdateRef updateRef) {
+ if (status == kCGDisplayStreamFrameStatusStopped) {
+ manager->DestroyStream(unique_id);
+ return;
+ }
+
+ if (manager->ShouldIgnoreUpdates())
+ return;
+
+ // Only pay attention to frame updates.
+ if (status != kCGDisplayStreamFrameStatusFrameComplete)
+ return;
+
+ size_t count = 0;
+ const CGRect* rects = CGDisplayStreamUpdateGetRects(
+ updateRef, kCGDisplayStreamUpdateDirtyRects, &count);
+ if (count != 0) {
+ // According to CGDisplayStream.h, it's safe to call
+ // CGDisplayStreamStop() from within the callback.
+ ScreenRefresh(count, rects, display_origin);
+ }
+ };
+ CGDisplayStreamRef display_stream = CGDisplayStreamCreate(
+ display_id, pixel_width, pixel_height, 'BGRA', nullptr, handler);
+
+ if (display_stream) {
+ CGError error = CGDisplayStreamStart(display_stream);
+ if (error != kCGErrorSuccess)
+ return false;
+
+ CFRunLoopSourceRef source =
+ CGDisplayStreamGetRunLoopSource(display_stream);
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopCommonModes);
+ display_stream_manager_->SaveStream(unique_id, display_stream);
+ }
+ }
+
+ return true;
+}
+
+void ScreenCapturerMac::UnregisterRefreshAndMoveHandlers() {
+ display_stream_manager_->UnregisterActiveStreams();
+}
+
+void ScreenCapturerMac::ScreenRefresh(CGRectCount count,
+ const CGRect* rect_array,
+ DesktopVector display_origin) {
+ if (screen_pixel_bounds_.is_empty())
+ ScreenConfigurationChanged();
+
+ // The refresh rects are in display coordinates. We want to translate to
+ // framebuffer coordinates. If a specific display is being captured, then no
+ // change is necessary. If all displays are being captured, then we want to
+ // translate by the origin of the display.
+ DesktopVector translate_vector;
+ if (!current_display_)
+ translate_vector = display_origin;
+
+ DesktopRegion region;
+ for (CGRectCount i = 0; i < count; ++i) {
+ // All rects are already in physical pixel coordinates.
+ DesktopRect rect = DesktopRect::MakeXYWH(
+ rect_array[i].origin.x, rect_array[i].origin.y,
+ rect_array[i].size.width, rect_array[i].size.height);
+
+ rect.Translate(translate_vector);
+
+ region.AddRect(rect);
+ }
+
+ helper_.InvalidateRegion(region);
+}
+
+std::unique_ptr<DesktopFrame> ScreenCapturerMac::CreateFrame() {
+ std::unique_ptr<DesktopFrame> frame(
+ new BasicDesktopFrame(screen_pixel_bounds_.size()));
+ frame->set_dpi(DesktopVector(kStandardDPI * dip_to_pixel_scale_,
+ kStandardDPI * dip_to_pixel_scale_));
+ return frame;
+}
+
+} // namespace
+
+// static
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options) {
+ if (!options.configuration_monitor())
+ return nullptr;
+
+ std::unique_ptr<ScreenCapturerMac> capturer(new ScreenCapturerMac(
+ options.configuration_monitor(), options.detect_updated_region()));
+ if (!capturer.get()->Init()) {
+ return nullptr;
+ }
+
+ return capturer;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_mac_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_mac_unittest.cc
new file mode 100644
index 0000000000..96e844066a
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_mac_unittest.cc
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <ApplicationServices/ApplicationServices.h>
+
+#include <memory>
+#include <ostream>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/mac/desktop_configuration.h"
+#include "modules/desktop_capture/mock_desktop_capturer_callback.h"
+#include "test/gtest.h"
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::Return;
+
+namespace webrtc {
+
+class ScreenCapturerMacTest : public ::testing::Test {
+ public:
+ // Verifies that the whole screen is initially dirty.
+ void CaptureDoneCallback1(DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame>* frame);
+
+ // Verifies that a rectangle explicitly marked as dirty is propagated
+ // correctly.
+ void CaptureDoneCallback2(DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame>* frame);
+
+ protected:
+ void SetUp() override {
+ capturer_ = DesktopCapturer::CreateScreenCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ }
+
+ std::unique_ptr<DesktopCapturer> capturer_;
+ MockDesktopCapturerCallback callback_;
+};
+
+void ScreenCapturerMacTest::CaptureDoneCallback1(
+ DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame>* frame) {
+ EXPECT_EQ(result, DesktopCapturer::Result::SUCCESS);
+
+ MacDesktopConfiguration config = MacDesktopConfiguration::GetCurrent(
+ MacDesktopConfiguration::BottomLeftOrigin);
+
+ // Verify that the region contains full frame.
+ DesktopRegion::Iterator it((*frame)->updated_region());
+ EXPECT_TRUE(!it.IsAtEnd() && it.rect().equals(config.pixel_bounds));
+}
+
+void ScreenCapturerMacTest::CaptureDoneCallback2(
+ DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame>* frame) {
+ EXPECT_EQ(result, DesktopCapturer::Result::SUCCESS);
+
+ MacDesktopConfiguration config = MacDesktopConfiguration::GetCurrent(
+ MacDesktopConfiguration::BottomLeftOrigin);
+ int width = config.pixel_bounds.width();
+ int height = config.pixel_bounds.height();
+
+ EXPECT_EQ(width, (*frame)->size().width());
+ EXPECT_EQ(height, (*frame)->size().height());
+ EXPECT_TRUE((*frame)->data() != NULL);
+ // Depending on the capture method, the screen may be flipped or not, so
+ // the stride may be positive or negative.
+ EXPECT_EQ(static_cast<int>(sizeof(uint32_t) * width),
+ abs((*frame)->stride()));
+}
+
+TEST_F(ScreenCapturerMacTest, Capture) {
+ EXPECT_CALL(callback_,
+ OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, _))
+ .Times(2)
+ .WillOnce(Invoke(this, &ScreenCapturerMacTest::CaptureDoneCallback1))
+ .WillOnce(Invoke(this, &ScreenCapturerMacTest::CaptureDoneCallback2));
+
+ SCOPED_TRACE("");
+ capturer_->Start(&callback_);
+
+ // Check that we get an initial full-screen updated.
+ capturer_->CaptureFrame();
+
+ // Check that subsequent dirty rects are propagated correctly.
+ capturer_->CaptureFrame();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_null.cc b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_null.cc
new file mode 100644
index 0000000000..6b1ccb322e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_null.cc
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_capturer.h"
+
+namespace webrtc {
+
+// static
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options) {
+ return nullptr;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_unittest.cc
new file mode 100644
index 0000000000..8f5fe631f1
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_unittest.cc
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/mock_desktop_capturer_callback.h"
+#include "rtc_base/logging.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+#if defined(WEBRTC_WIN)
+#include "modules/desktop_capture/win/screen_capturer_win_directx.h"
+#endif // defined(WEBRTC_WIN)
+
+using ::testing::_;
+
+const int kTestSharedMemoryId = 123;
+
+namespace webrtc {
+
+class ScreenCapturerTest : public ::testing::Test {
+ public:
+ void SetUp() override {
+ capturer_ = DesktopCapturer::CreateScreenCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ ASSERT_TRUE(capturer_);
+ }
+
+ protected:
+#if defined(WEBRTC_WIN)
+ // Enable allow_directx_capturer in DesktopCaptureOptions, but let
+ // DesktopCapturer::CreateScreenCapturer to decide whether a DirectX capturer
+ // should be used.
+ void MaybeCreateDirectxCapturer() {
+ DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault());
+ options.set_allow_directx_capturer(true);
+ capturer_ = DesktopCapturer::CreateScreenCapturer(options);
+ }
+
+ bool CreateDirectxCapturer() {
+ if (!ScreenCapturerWinDirectx::IsSupported()) {
+ RTC_LOG(LS_WARNING) << "Directx capturer is not supported";
+ return false;
+ }
+
+ MaybeCreateDirectxCapturer();
+ return true;
+ }
+
+ void CreateMagnifierCapturer() {
+ DesktopCaptureOptions options(DesktopCaptureOptions::CreateDefault());
+ options.set_allow_use_magnification_api(true);
+ capturer_ = DesktopCapturer::CreateScreenCapturer(options);
+ }
+#endif // defined(WEBRTC_WIN)
+
+ std::unique_ptr<DesktopCapturer> capturer_;
+ MockDesktopCapturerCallback callback_;
+};
+
+class FakeSharedMemory : public SharedMemory {
+ public:
+ FakeSharedMemory(char* buffer, size_t size)
+ : SharedMemory(buffer, size, 0, kTestSharedMemoryId), buffer_(buffer) {}
+ ~FakeSharedMemory() override { delete[] buffer_; }
+
+ FakeSharedMemory(const FakeSharedMemory&) = delete;
+ FakeSharedMemory& operator=(const FakeSharedMemory&) = delete;
+
+ private:
+ char* buffer_;
+};
+
+class FakeSharedMemoryFactory : public SharedMemoryFactory {
+ public:
+ FakeSharedMemoryFactory() {}
+ ~FakeSharedMemoryFactory() override {}
+
+ FakeSharedMemoryFactory(const FakeSharedMemoryFactory&) = delete;
+ FakeSharedMemoryFactory& operator=(const FakeSharedMemoryFactory&) = delete;
+
+ std::unique_ptr<SharedMemory> CreateSharedMemory(size_t size) override {
+ return std::unique_ptr<SharedMemory>(
+ new FakeSharedMemory(new char[size], size));
+ }
+};
+
+ACTION_P(SaveUniquePtrArg, dest) {
+ *dest = std::move(*arg1);
+}
+
+// TODO(bugs.webrtc.org/12950): Re-enable when libc++ issue is fixed.
+#if defined(WEBRTC_LINUX) && defined(MEMORY_SANITIZER)
+#define MAYBE_GetScreenListAndSelectScreen DISABLED_GetScreenListAndSelectScreen
+#else
+#define MAYBE_GetScreenListAndSelectScreen GetScreenListAndSelectScreen
+#endif
+TEST_F(ScreenCapturerTest, MAYBE_GetScreenListAndSelectScreen) {
+ webrtc::DesktopCapturer::SourceList screens;
+ EXPECT_TRUE(capturer_->GetSourceList(&screens));
+ for (const auto& screen : screens) {
+ EXPECT_TRUE(capturer_->SelectSource(screen.id));
+ }
+}
+
+// Flaky on Linux. See: crbug.com/webrtc/7830
+#if defined(WEBRTC_LINUX)
+#define MAYBE_StartCapturer DISABLED_StartCaptuerer
+#else
+#define MAYBE_StartCapturer StartCapturer
+#endif
+TEST_F(ScreenCapturerTest, MAYBE_StartCapturer) {
+ capturer_->Start(&callback_);
+}
+
+#if defined(WEBRTC_LINUX)
+#define MAYBE_Capture DISABLED_Capture
+#else
+#define MAYBE_Capture Capture
+#endif
+TEST_F(ScreenCapturerTest, MAYBE_Capture) {
+ // Assume that Start() treats the screen as invalid initially.
+ std::unique_ptr<DesktopFrame> frame;
+ EXPECT_CALL(callback_,
+ OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, _))
+ .WillOnce(SaveUniquePtrArg(&frame));
+
+ capturer_->Start(&callback_);
+ capturer_->CaptureFrame();
+
+ ASSERT_TRUE(frame);
+ EXPECT_GT(frame->size().width(), 0);
+ EXPECT_GT(frame->size().height(), 0);
+ EXPECT_GE(frame->stride(),
+ frame->size().width() * DesktopFrame::kBytesPerPixel);
+ EXPECT_TRUE(frame->shared_memory() == NULL);
+
+ // Verify that the region contains whole screen.
+ EXPECT_FALSE(frame->updated_region().is_empty());
+ DesktopRegion::Iterator it(frame->updated_region());
+ ASSERT_TRUE(!it.IsAtEnd());
+ EXPECT_TRUE(it.rect().equals(DesktopRect::MakeSize(frame->size())));
+ it.Advance();
+ EXPECT_TRUE(it.IsAtEnd());
+}
+
+#if defined(WEBRTC_WIN)
+
+TEST_F(ScreenCapturerTest, UseSharedBuffers) {
+ std::unique_ptr<DesktopFrame> frame;
+ EXPECT_CALL(callback_,
+ OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, _))
+ .WillOnce(SaveUniquePtrArg(&frame));
+
+ capturer_->Start(&callback_);
+ capturer_->SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory>(new FakeSharedMemoryFactory()));
+ capturer_->CaptureFrame();
+
+ ASSERT_TRUE(frame);
+ ASSERT_TRUE(frame->shared_memory());
+ EXPECT_EQ(frame->shared_memory()->id(), kTestSharedMemoryId);
+}
+
+TEST_F(ScreenCapturerTest, UseMagnifier) {
+ CreateMagnifierCapturer();
+ std::unique_ptr<DesktopFrame> frame;
+ EXPECT_CALL(callback_,
+ OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, _))
+ .WillOnce(SaveUniquePtrArg(&frame));
+
+ capturer_->Start(&callback_);
+ capturer_->CaptureFrame();
+ ASSERT_TRUE(frame);
+}
+
+TEST_F(ScreenCapturerTest, UseDirectxCapturer) {
+ if (!CreateDirectxCapturer()) {
+ return;
+ }
+
+ std::unique_ptr<DesktopFrame> frame;
+ EXPECT_CALL(callback_,
+ OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, _))
+ .WillOnce(SaveUniquePtrArg(&frame));
+
+ capturer_->Start(&callback_);
+ capturer_->CaptureFrame();
+ ASSERT_TRUE(frame);
+}
+
+TEST_F(ScreenCapturerTest, UseDirectxCapturerWithSharedBuffers) {
+ if (!CreateDirectxCapturer()) {
+ return;
+ }
+
+ std::unique_ptr<DesktopFrame> frame;
+ EXPECT_CALL(callback_,
+ OnCaptureResultPtr(DesktopCapturer::Result::SUCCESS, _))
+ .WillOnce(SaveUniquePtrArg(&frame));
+
+ capturer_->Start(&callback_);
+ capturer_->SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory>(new FakeSharedMemoryFactory()));
+ capturer_->CaptureFrame();
+ ASSERT_TRUE(frame);
+ ASSERT_TRUE(frame->shared_memory());
+ EXPECT_EQ(frame->shared_memory()->id(), kTestSharedMemoryId);
+}
+
+#endif // defined(WEBRTC_WIN)
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_capturer_win.cc b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_win.cc
new file mode 100644
index 0000000000..b5935dc316
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_capturer_win.cc
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <utility>
+
+#include "modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/fallback_desktop_capturer_wrapper.h"
+#include "modules/desktop_capture/rgba_color.h"
+#include "modules/desktop_capture/win/screen_capturer_win_directx.h"
+#include "modules/desktop_capture/win/screen_capturer_win_gdi.h"
+#include "modules/desktop_capture/win/screen_capturer_win_magnifier.h"
+
+namespace webrtc {
+
+namespace {
+
+std::unique_ptr<DesktopCapturer> CreateScreenCapturerWinDirectx() {
+ std::unique_ptr<DesktopCapturer> capturer(new ScreenCapturerWinDirectx());
+ capturer.reset(new BlankDetectorDesktopCapturerWrapper(
+ std::move(capturer), RgbaColor(0, 0, 0, 0)));
+ return capturer;
+}
+
+} // namespace
+
+// static
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options) {
+ std::unique_ptr<DesktopCapturer> capturer(new ScreenCapturerWinGdi(options));
+ if (options.allow_directx_capturer()) {
+ // `dxgi_duplicator_controller` should be alive in this scope to ensure it
+ // won't unload DxgiDuplicatorController.
+ auto dxgi_duplicator_controller = DxgiDuplicatorController::Instance();
+ if (ScreenCapturerWinDirectx::IsSupported()) {
+ capturer.reset(new FallbackDesktopCapturerWrapper(
+ CreateScreenCapturerWinDirectx(), std::move(capturer)));
+ }
+ }
+
+ if (options.allow_use_magnification_api()) {
+ // ScreenCapturerWinMagnifier cannot work on Windows XP or earlier, as well
+ // as 64-bit only Windows, and it may randomly crash on multi-screen
+ // systems. So we may need to fallback to use original capturer.
+ capturer.reset(new FallbackDesktopCapturerWrapper(
+ std::unique_ptr<DesktopCapturer>(new ScreenCapturerWinMagnifier()),
+ std::move(capturer)));
+ }
+
+ return capturer;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_drawer.cc b/third_party/libwebrtc/modules/desktop_capture/screen_drawer.cc
new file mode 100644
index 0000000000..6460f19f65
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_drawer.cc
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/screen_drawer.h"
+
+namespace webrtc {
+
+namespace {
+std::unique_ptr<ScreenDrawerLock> g_screen_drawer_lock;
+} // namespace
+
+ScreenDrawerLock::ScreenDrawerLock() = default;
+ScreenDrawerLock::~ScreenDrawerLock() = default;
+
+ScreenDrawer::ScreenDrawer() {
+ g_screen_drawer_lock = ScreenDrawerLock::Create();
+}
+
+ScreenDrawer::~ScreenDrawer() {
+ g_screen_drawer_lock.reset();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_drawer.h b/third_party/libwebrtc/modules/desktop_capture/screen_drawer.h
new file mode 100644
index 0000000000..ad7c0ad8d1
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_drawer.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_SCREEN_DRAWER_H_
+#define MODULES_DESKTOP_CAPTURE_SCREEN_DRAWER_H_
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/rgba_color.h"
+
+namespace webrtc {
+
+// A cross-process lock to ensure only one ScreenDrawer can be used at a certain
+// time.
+class ScreenDrawerLock {
+ public:
+ virtual ~ScreenDrawerLock();
+
+ static std::unique_ptr<ScreenDrawerLock> Create();
+
+ protected:
+ ScreenDrawerLock();
+};
+
+// A set of basic platform dependent functions to draw various shapes on the
+// screen.
+class ScreenDrawer {
+ public:
+ // Creates a ScreenDrawer for the current platform, returns nullptr if no
+ // ScreenDrawer implementation available.
+ // If the implementation cannot guarantee two ScreenDrawer instances won't
+ // impact each other, this function may block current thread until another
+ // ScreenDrawer has been destroyed.
+ static std::unique_ptr<ScreenDrawer> Create();
+
+ ScreenDrawer();
+ virtual ~ScreenDrawer();
+
+ // Returns the region inside which DrawRectangle() function are expected to
+ // work, in capturer coordinates (assuming ScreenCapturer::SelectScreen has
+ // not been called). This region may exclude regions of the screen reserved by
+ // the OS for things like menu bars or app launchers. The DesktopRect is in
+ // system coordinate, i.e. the primary monitor always starts from (0, 0).
+ virtual DesktopRect DrawableRegion() = 0;
+
+ // Draws a rectangle to cover `rect` with `color`. Note, rect.bottom() and
+ // rect.right() two lines are not included. The part of `rect` which is out of
+ // DrawableRegion() will be ignored.
+ virtual void DrawRectangle(DesktopRect rect, RgbaColor color) = 0;
+
+ // Clears all content on the screen by filling the area with black.
+ virtual void Clear() = 0;
+
+ // Blocks current thread until OS finishes previous DrawRectangle() actions.
+ // ScreenCapturer should be able to capture the changes after this function
+ // finish.
+ virtual void WaitForPendingDraws() = 0;
+
+ // Returns true if incomplete shapes previous actions required may be drawn on
+ // the screen after a WaitForPendingDraws() call. i.e. Though the complete
+ // shapes will eventually be drawn on the screen, due to some OS limitations,
+ // these shapes may be partially appeared sometimes.
+ virtual bool MayDrawIncompleteShapes() = 0;
+
+ // Returns the id of the drawer window. This function returns kNullWindowId if
+ // the implementation does not draw on a window of the system.
+ virtual WindowId window_id() const = 0;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_SCREEN_DRAWER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_drawer_linux.cc b/third_party/libwebrtc/modules/desktop_capture/screen_drawer_linux.cc
new file mode 100644
index 0000000000..fce036b4aa
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_drawer_linux.cc
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#include <string.h>
+
+#include <memory>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/linux/x11/shared_x_display.h"
+#include "modules/desktop_capture/rgba_color.h"
+#include "modules/desktop_capture/screen_drawer.h"
+#include "modules/desktop_capture/screen_drawer_lock_posix.h"
+#include "rtc_base/checks.h"
+#include "system_wrappers/include/sleep.h"
+
+namespace webrtc {
+
+namespace {
+
+// A ScreenDrawer implementation for X11.
+class ScreenDrawerLinux : public ScreenDrawer {
+ public:
+ ScreenDrawerLinux();
+ ~ScreenDrawerLinux() override;
+
+ // ScreenDrawer interface.
+ DesktopRect DrawableRegion() override;
+ void DrawRectangle(DesktopRect rect, RgbaColor color) override;
+ void Clear() override;
+ void WaitForPendingDraws() override;
+ bool MayDrawIncompleteShapes() override;
+ WindowId window_id() const override;
+
+ private:
+ // Bring the window to the front, this can help to avoid the impact from other
+ // windows or shadow effect.
+ void BringToFront();
+
+ rtc::scoped_refptr<SharedXDisplay> display_;
+ int screen_num_;
+ DesktopRect rect_;
+ Window window_;
+ GC context_;
+ Colormap colormap_;
+};
+
+ScreenDrawerLinux::ScreenDrawerLinux() {
+ display_ = SharedXDisplay::CreateDefault();
+ RTC_CHECK(display_.get());
+ screen_num_ = DefaultScreen(display_->display());
+ XWindowAttributes root_attributes;
+ if (!XGetWindowAttributes(display_->display(),
+ RootWindow(display_->display(), screen_num_),
+ &root_attributes)) {
+ RTC_DCHECK_NOTREACHED() << "Failed to get root window size.";
+ }
+ window_ = XCreateSimpleWindow(
+ display_->display(), RootWindow(display_->display(), screen_num_), 0, 0,
+ root_attributes.width, root_attributes.height, 0,
+ BlackPixel(display_->display(), screen_num_),
+ BlackPixel(display_->display(), screen_num_));
+ XSelectInput(display_->display(), window_, StructureNotifyMask);
+ XMapWindow(display_->display(), window_);
+ while (true) {
+ XEvent event;
+ XNextEvent(display_->display(), &event);
+ if (event.type == MapNotify) {
+ break;
+ }
+ }
+ XFlush(display_->display());
+ Window child;
+ int x, y;
+ if (!XTranslateCoordinates(display_->display(), window_,
+ RootWindow(display_->display(), screen_num_), 0, 0,
+ &x, &y, &child)) {
+ RTC_DCHECK_NOTREACHED() << "Failed to get window position.";
+ }
+ // Some window manager does not allow a window to cover two or more monitors.
+ // So if the window is on the first monitor of a two-monitor system, the
+ // second half won't be able to show up without changing configurations of WM,
+ // and its DrawableRegion() is not accurate.
+ rect_ = DesktopRect::MakeLTRB(x, y, root_attributes.width,
+ root_attributes.height);
+ context_ = DefaultGC(display_->display(), screen_num_);
+ colormap_ = DefaultColormap(display_->display(), screen_num_);
+ BringToFront();
+ // Wait for window animations.
+ SleepMs(200);
+}
+
+ScreenDrawerLinux::~ScreenDrawerLinux() {
+ XUnmapWindow(display_->display(), window_);
+ XDestroyWindow(display_->display(), window_);
+}
+
+DesktopRect ScreenDrawerLinux::DrawableRegion() {
+ return rect_;
+}
+
+void ScreenDrawerLinux::DrawRectangle(DesktopRect rect, RgbaColor color) {
+ rect.Translate(-rect_.left(), -rect_.top());
+ XColor xcolor;
+ // X11 does not support Alpha.
+ // X11 uses 16 bits for each primary color, so we need to slightly normalize
+ // a 8 bits channel to 16 bits channel, by setting the low 8 bits as its high
+ // 8 bits to avoid a mismatch of color returned by capturer.
+ xcolor.red = (color.red << 8) + color.red;
+ xcolor.green = (color.green << 8) + color.green;
+ xcolor.blue = (color.blue << 8) + color.blue;
+ xcolor.flags = DoRed | DoGreen | DoBlue;
+ XAllocColor(display_->display(), colormap_, &xcolor);
+ XSetForeground(display_->display(), context_, xcolor.pixel);
+ XFillRectangle(display_->display(), window_, context_, rect.left(),
+ rect.top(), rect.width(), rect.height());
+ XFlush(display_->display());
+}
+
+void ScreenDrawerLinux::Clear() {
+ DrawRectangle(rect_, RgbaColor(0, 0, 0));
+}
+
+// TODO(zijiehe): Find the right signal from X11 to indicate the finish of all
+// pending paintings.
+void ScreenDrawerLinux::WaitForPendingDraws() {
+ SleepMs(50);
+}
+
+bool ScreenDrawerLinux::MayDrawIncompleteShapes() {
+ return true;
+}
+
+WindowId ScreenDrawerLinux::window_id() const {
+ return window_;
+}
+
+void ScreenDrawerLinux::BringToFront() {
+ Atom state_above = XInternAtom(display_->display(), "_NET_WM_STATE_ABOVE", 1);
+ Atom window_state = XInternAtom(display_->display(), "_NET_WM_STATE", 1);
+ if (state_above == None || window_state == None) {
+ // Fallback to use XRaiseWindow, it's not reliable if two windows are both
+ // raise itself to the top.
+ XRaiseWindow(display_->display(), window_);
+ return;
+ }
+
+ XEvent event;
+ memset(&event, 0, sizeof(event));
+ event.type = ClientMessage;
+ event.xclient.window = window_;
+ event.xclient.message_type = window_state;
+ event.xclient.format = 32;
+ event.xclient.data.l[0] = 1; // _NET_WM_STATE_ADD
+ event.xclient.data.l[1] = state_above;
+ XSendEvent(display_->display(), RootWindow(display_->display(), screen_num_),
+ False, SubstructureRedirectMask | SubstructureNotifyMask, &event);
+}
+
+} // namespace
+
+// static
+std::unique_ptr<ScreenDrawerLock> ScreenDrawerLock::Create() {
+ return std::make_unique<ScreenDrawerLockPosix>();
+}
+
+// static
+std::unique_ptr<ScreenDrawer> ScreenDrawer::Create() {
+ if (SharedXDisplay::CreateDefault().get()) {
+ return std::make_unique<ScreenDrawerLinux>();
+ }
+ return nullptr;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_drawer_lock_posix.cc b/third_party/libwebrtc/modules/desktop_capture/screen_drawer_lock_posix.cc
new file mode 100644
index 0000000000..28cb501fe7
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_drawer_lock_posix.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/screen_drawer_lock_posix.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "absl/strings/string_view.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+namespace {
+
+// A uuid as the name of semaphore.
+static constexpr char kSemaphoreName[] = "GSDL54fe5552804711e6a7253f429a";
+
+} // namespace
+
+ScreenDrawerLockPosix::ScreenDrawerLockPosix()
+ : ScreenDrawerLockPosix(kSemaphoreName) {}
+
+ScreenDrawerLockPosix::ScreenDrawerLockPosix(const char* name) {
+ semaphore_ = sem_open(name, O_CREAT, S_IRWXU | S_IRWXG | S_IRWXO, 1);
+ if (semaphore_ == SEM_FAILED) {
+ RTC_LOG_ERRNO(LS_ERROR) << "Failed to create named semaphore with " << name;
+ RTC_DCHECK_NOTREACHED();
+ }
+
+ sem_wait(semaphore_);
+}
+
+ScreenDrawerLockPosix::~ScreenDrawerLockPosix() {
+ if (semaphore_ == SEM_FAILED) {
+ return;
+ }
+
+ sem_post(semaphore_);
+ sem_close(semaphore_);
+ // sem_unlink a named semaphore won't wait until other clients to release the
+ // sem_t. So if a new process starts, it will sem_open a different kernel
+ // object with the same name and eventually breaks the cross-process lock.
+}
+
+// static
+void ScreenDrawerLockPosix::Unlink(absl::string_view name) {
+ sem_unlink(std::string(name).c_str());
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_drawer_lock_posix.h b/third_party/libwebrtc/modules/desktop_capture/screen_drawer_lock_posix.h
new file mode 100644
index 0000000000..13899b2d75
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_drawer_lock_posix.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_SCREEN_DRAWER_LOCK_POSIX_H_
+#define MODULES_DESKTOP_CAPTURE_SCREEN_DRAWER_LOCK_POSIX_H_
+
+#include <semaphore.h>
+
+#include "absl/strings/string_view.h"
+#include "modules/desktop_capture/screen_drawer.h"
+
+namespace webrtc {
+
+class ScreenDrawerLockPosix final : public ScreenDrawerLock {
+ public:
+ ScreenDrawerLockPosix();
+ // Provides a name other than the default one for test only.
+ explicit ScreenDrawerLockPosix(const char* name);
+ ~ScreenDrawerLockPosix() override;
+
+ // Unlinks the named semaphore actively. This will remove the sem_t object in
+ // the system and allow others to create a different sem_t object with the
+ // same/ name.
+ static void Unlink(absl::string_view name);
+
+ private:
+ sem_t* semaphore_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_SCREEN_DRAWER_LOCK_POSIX_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_drawer_mac.cc b/third_party/libwebrtc/modules/desktop_capture/screen_drawer_mac.cc
new file mode 100644
index 0000000000..17719e4439
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_drawer_mac.cc
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+// TODO(zijiehe): Implement ScreenDrawerMac
+
+#include <memory>
+
+#include "modules/desktop_capture/screen_drawer.h"
+#include "modules/desktop_capture/screen_drawer_lock_posix.h"
+
+namespace webrtc {
+
+// static
+std::unique_ptr<ScreenDrawerLock> ScreenDrawerLock::Create() {
+ return std::make_unique<ScreenDrawerLockPosix>();
+}
+
+// static
+std::unique_ptr<ScreenDrawer> ScreenDrawer::Create() {
+ return nullptr;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_drawer_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/screen_drawer_unittest.cc
new file mode 100644
index 0000000000..584770dbf8
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_drawer_unittest.cc
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/screen_drawer.h"
+
+#include <stdint.h>
+
+#include <atomic>
+#include <memory>
+
+#include "api/function_view.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/platform_thread.h"
+#include "rtc_base/random.h"
+#include "rtc_base/time_utils.h"
+#include "system_wrappers/include/sleep.h"
+#include "test/gtest.h"
+
+#if defined(WEBRTC_POSIX)
+#include "modules/desktop_capture/screen_drawer_lock_posix.h"
+#endif
+
+namespace webrtc {
+
+namespace {
+
+void TestScreenDrawerLock(
+ rtc::FunctionView<std::unique_ptr<ScreenDrawerLock>()> ctor) {
+ constexpr int kLockDurationMs = 100;
+
+ std::atomic<bool> created(false);
+ std::atomic<bool> ready(false);
+
+ class Task {
+ public:
+ Task(std::atomic<bool>* created,
+ const std::atomic<bool>& ready,
+ rtc::FunctionView<std::unique_ptr<ScreenDrawerLock>()> ctor)
+ : created_(created), ready_(ready), ctor_(ctor) {}
+
+ ~Task() = default;
+
+ void RunTask() {
+ std::unique_ptr<ScreenDrawerLock> lock = ctor_();
+ ASSERT_TRUE(!!lock);
+ created_->store(true);
+ // Wait for the main thread to get the signal of created_.
+ while (!ready_.load()) {
+ SleepMs(1);
+ }
+ // At this point, main thread should begin to create a second lock. Though
+ // it's still possible the second lock won't be created before the
+ // following sleep has been finished, the possibility will be
+ // significantly reduced.
+ const int64_t current_ms = rtc::TimeMillis();
+ // SleepMs() may return early. See
+ // https://cs.chromium.org/chromium/src/third_party/webrtc/system_wrappers/include/sleep.h?rcl=4a604c80cecce18aff6fc5e16296d04675312d83&l=20
+ // But we need to ensure at least 100 ms has been passed before unlocking
+ // `lock`.
+ while (rtc::TimeMillis() - current_ms < kLockDurationMs) {
+ SleepMs(kLockDurationMs - (rtc::TimeMillis() - current_ms));
+ }
+ }
+
+ private:
+ std::atomic<bool>* const created_;
+ const std::atomic<bool>& ready_;
+ const rtc::FunctionView<std::unique_ptr<ScreenDrawerLock>()> ctor_;
+ } task(&created, ready, ctor);
+
+ auto lock_thread = rtc::PlatformThread::SpawnJoinable(
+ [&task] { task.RunTask(); }, "lock_thread");
+
+ // Wait for the first lock in Task::RunTask() to be created.
+ // TODO(zijiehe): Find a better solution to wait for the creation of the first
+ // lock. See
+ // https://chromium-review.googlesource.com/c/607688/13/webrtc/modules/desktop_capture/screen_drawer_unittest.cc
+ while (!created.load()) {
+ SleepMs(1);
+ }
+
+ const int64_t start_ms = rtc::TimeMillis();
+ ready.store(true);
+ // This is unlikely to fail, but just in case current thread is too laggy and
+ // cause the SleepMs() in RunTask() to finish before we creating another lock.
+ ASSERT_GT(kLockDurationMs, rtc::TimeMillis() - start_ms);
+ ctor();
+ ASSERT_LE(kLockDurationMs, rtc::TimeMillis() - start_ms);
+}
+
+} // namespace
+
+// These are a set of manual test cases, as we do not have an automatical way to
+// detect whether a ScreenDrawer on a certain platform works well without
+// ScreenCapturer(s). So you may execute these test cases with
+// --gtest_also_run_disabled_tests --gtest_filter=ScreenDrawerTest.*.
+TEST(ScreenDrawerTest, DISABLED_DrawRectangles) {
+ std::unique_ptr<ScreenDrawer> drawer = ScreenDrawer::Create();
+ if (!drawer) {
+ RTC_LOG(LS_WARNING)
+ << "No ScreenDrawer implementation for current platform.";
+ return;
+ }
+
+ if (drawer->DrawableRegion().is_empty()) {
+ RTC_LOG(LS_WARNING)
+ << "ScreenDrawer of current platform does not provide a "
+ "non-empty DrawableRegion().";
+ return;
+ }
+
+ DesktopRect rect = drawer->DrawableRegion();
+ Random random(rtc::TimeMicros());
+ for (int i = 0; i < 100; i++) {
+ // Make sure we at least draw one pixel.
+ int left = random.Rand(rect.left(), rect.right() - 2);
+ int top = random.Rand(rect.top(), rect.bottom() - 2);
+ drawer->DrawRectangle(
+ DesktopRect::MakeLTRB(left, top, random.Rand(left + 1, rect.right()),
+ random.Rand(top + 1, rect.bottom())),
+ RgbaColor(random.Rand<uint8_t>(), random.Rand<uint8_t>(),
+ random.Rand<uint8_t>(), random.Rand<uint8_t>()));
+
+ if (i == 50) {
+ SleepMs(10000);
+ }
+ }
+
+ SleepMs(10000);
+}
+
+#if defined(THREAD_SANITIZER) // bugs.webrtc.org/10019
+#define MAYBE_TwoScreenDrawerLocks DISABLED_TwoScreenDrawerLocks
+#else
+#define MAYBE_TwoScreenDrawerLocks TwoScreenDrawerLocks
+#endif
+TEST(ScreenDrawerTest, MAYBE_TwoScreenDrawerLocks) {
+#if defined(WEBRTC_POSIX)
+ // ScreenDrawerLockPosix won't be able to unlink the named semaphore. So use a
+ // different semaphore name here to avoid deadlock.
+ const char* semaphore_name = "GSDL8784541a812011e788ff67427b";
+ ScreenDrawerLockPosix::Unlink(semaphore_name);
+
+ TestScreenDrawerLock([semaphore_name]() {
+ return std::make_unique<ScreenDrawerLockPosix>(semaphore_name);
+ });
+#elif defined(WEBRTC_WIN)
+ TestScreenDrawerLock([]() { return ScreenDrawerLock::Create(); });
+#endif
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/screen_drawer_win.cc b/third_party/libwebrtc/modules/desktop_capture/screen_drawer_win.cc
new file mode 100644
index 0000000000..7cf634fe89
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/screen_drawer_win.cc
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <windows.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/screen_drawer.h"
+#include "system_wrappers/include/sleep.h"
+
+namespace webrtc {
+
+namespace {
+
+static constexpr TCHAR kMutexName[] =
+ TEXT("Local\\ScreenDrawerWin-da834f82-8044-11e6-ac81-73dcdd1c1869");
+
+class ScreenDrawerLockWin : public ScreenDrawerLock {
+ public:
+ ScreenDrawerLockWin();
+ ~ScreenDrawerLockWin() override;
+
+ private:
+ HANDLE mutex_;
+};
+
+ScreenDrawerLockWin::ScreenDrawerLockWin() {
+ while (true) {
+ mutex_ = CreateMutex(NULL, FALSE, kMutexName);
+ if (GetLastError() != ERROR_ALREADY_EXISTS && mutex_ != NULL) {
+ break;
+ } else {
+ if (mutex_) {
+ CloseHandle(mutex_);
+ }
+ SleepMs(1000);
+ }
+ }
+}
+
+ScreenDrawerLockWin::~ScreenDrawerLockWin() {
+ CloseHandle(mutex_);
+}
+
+DesktopRect GetScreenRect() {
+ HDC hdc = GetDC(NULL);
+ DesktopRect rect = DesktopRect::MakeWH(GetDeviceCaps(hdc, HORZRES),
+ GetDeviceCaps(hdc, VERTRES));
+ ReleaseDC(NULL, hdc);
+ return rect;
+}
+
+HWND CreateDrawerWindow(DesktopRect rect) {
+ HWND hwnd = CreateWindowA(
+ "STATIC", "DrawerWindow", WS_POPUPWINDOW | WS_VISIBLE, rect.left(),
+ rect.top(), rect.width(), rect.height(), NULL, NULL, NULL, NULL);
+ SetForegroundWindow(hwnd);
+ return hwnd;
+}
+
+COLORREF ColorToRef(RgbaColor color) {
+ // Windows device context does not support alpha.
+ return RGB(color.red, color.green, color.blue);
+}
+
+// A ScreenDrawer implementation for Windows.
+class ScreenDrawerWin : public ScreenDrawer {
+ public:
+ ScreenDrawerWin();
+ ~ScreenDrawerWin() override;
+
+ // ScreenDrawer interface.
+ DesktopRect DrawableRegion() override;
+ void DrawRectangle(DesktopRect rect, RgbaColor color) override;
+ void Clear() override;
+ void WaitForPendingDraws() override;
+ bool MayDrawIncompleteShapes() override;
+ WindowId window_id() const override;
+
+ private:
+ // Bring the window to the front, this can help to avoid the impact from other
+ // windows or shadow effects.
+ void BringToFront();
+
+ // Draw a line with `color`.
+ void DrawLine(DesktopVector start, DesktopVector end, RgbaColor color);
+
+ // Draw a dot with `color`.
+ void DrawDot(DesktopVector vect, RgbaColor color);
+
+ const DesktopRect rect_;
+ HWND window_;
+ HDC hdc_;
+};
+
+ScreenDrawerWin::ScreenDrawerWin()
+ : ScreenDrawer(),
+ rect_(GetScreenRect()),
+ window_(CreateDrawerWindow(rect_)),
+ hdc_(GetWindowDC(window_)) {
+ // We do not need to handle any messages for the `window_`, so disable Windows
+ // from processing windows ghosting feature.
+ DisableProcessWindowsGhosting();
+
+ // Always use stock pen (DC_PEN) and brush (DC_BRUSH).
+ SelectObject(hdc_, GetStockObject(DC_PEN));
+ SelectObject(hdc_, GetStockObject(DC_BRUSH));
+ BringToFront();
+}
+
+ScreenDrawerWin::~ScreenDrawerWin() {
+ ReleaseDC(NULL, hdc_);
+ DestroyWindow(window_);
+ // Unfortunately there is no EnableProcessWindowsGhosting() API.
+}
+
+DesktopRect ScreenDrawerWin::DrawableRegion() {
+ return rect_;
+}
+
+void ScreenDrawerWin::DrawRectangle(DesktopRect rect, RgbaColor color) {
+ if (rect.width() == 1 && rect.height() == 1) {
+ // Rectangle function cannot draw a 1 pixel rectangle.
+ DrawDot(rect.top_left(), color);
+ return;
+ }
+
+ if (rect.width() == 1 || rect.height() == 1) {
+ // Rectangle function cannot draw a 1 pixel rectangle.
+ DrawLine(rect.top_left(), DesktopVector(rect.right(), rect.bottom()),
+ color);
+ return;
+ }
+
+ SetDCBrushColor(hdc_, ColorToRef(color));
+ SetDCPenColor(hdc_, ColorToRef(color));
+ Rectangle(hdc_, rect.left(), rect.top(), rect.right(), rect.bottom());
+}
+
+void ScreenDrawerWin::Clear() {
+ DrawRectangle(rect_, RgbaColor(0, 0, 0));
+}
+
+// TODO(zijiehe): Find the right signal to indicate the finish of all pending
+// paintings.
+void ScreenDrawerWin::WaitForPendingDraws() {
+ BringToFront();
+ SleepMs(50);
+}
+
+bool ScreenDrawerWin::MayDrawIncompleteShapes() {
+ return true;
+}
+
+WindowId ScreenDrawerWin::window_id() const {
+ return reinterpret_cast<WindowId>(window_);
+}
+
+void ScreenDrawerWin::DrawLine(DesktopVector start,
+ DesktopVector end,
+ RgbaColor color) {
+ POINT points[2];
+ points[0].x = start.x();
+ points[0].y = start.y();
+ points[1].x = end.x();
+ points[1].y = end.y();
+ SetDCPenColor(hdc_, ColorToRef(color));
+ Polyline(hdc_, points, 2);
+}
+
+void ScreenDrawerWin::DrawDot(DesktopVector vect, RgbaColor color) {
+ SetPixel(hdc_, vect.x(), vect.y(), ColorToRef(color));
+}
+
+void ScreenDrawerWin::BringToFront() {
+ if (SetWindowPos(window_, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE) != FALSE) {
+ return;
+ }
+
+ long ex_style = GetWindowLong(window_, GWL_EXSTYLE);
+ ex_style |= WS_EX_TOPMOST;
+ if (SetWindowLong(window_, GWL_EXSTYLE, ex_style) != 0) {
+ return;
+ }
+
+ BringWindowToTop(window_);
+}
+
+} // namespace
+
+// static
+std::unique_ptr<ScreenDrawerLock> ScreenDrawerLock::Create() {
+ return std::unique_ptr<ScreenDrawerLock>(new ScreenDrawerLockWin());
+}
+
+// static
+std::unique_ptr<ScreenDrawer> ScreenDrawer::Create() {
+ return std::unique_ptr<ScreenDrawer>(new ScreenDrawerWin());
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/shared_desktop_frame.cc b/third_party/libwebrtc/modules/desktop_capture/shared_desktop_frame.cc
new file mode 100644
index 0000000000..e374038cbc
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/shared_desktop_frame.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/shared_desktop_frame.h"
+
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+namespace webrtc {
+
+SharedDesktopFrame::~SharedDesktopFrame() {}
+
+// static
+std::unique_ptr<SharedDesktopFrame> SharedDesktopFrame::Wrap(
+ std::unique_ptr<DesktopFrame> desktop_frame) {
+ return std::unique_ptr<SharedDesktopFrame>(new SharedDesktopFrame(
+ rtc::scoped_refptr<Core>(new Core(std::move(desktop_frame)))));
+}
+
+SharedDesktopFrame* SharedDesktopFrame::Wrap(DesktopFrame* desktop_frame) {
+ return Wrap(std::unique_ptr<DesktopFrame>(desktop_frame)).release();
+}
+
+DesktopFrame* SharedDesktopFrame::GetUnderlyingFrame() {
+ return core_->get();
+}
+
+bool SharedDesktopFrame::ShareFrameWith(const SharedDesktopFrame& other) const {
+ return core_->get() == other.core_->get();
+}
+
+std::unique_ptr<SharedDesktopFrame> SharedDesktopFrame::Share() {
+ std::unique_ptr<SharedDesktopFrame> result(new SharedDesktopFrame(core_));
+ result->CopyFrameInfoFrom(*this);
+ return result;
+}
+
+bool SharedDesktopFrame::IsShared() {
+ return !core_->HasOneRef();
+}
+
+SharedDesktopFrame::SharedDesktopFrame(rtc::scoped_refptr<Core> core)
+ : DesktopFrame((*core)->size(),
+ (*core)->stride(),
+ (*core)->data(),
+ (*core)->shared_memory()),
+ core_(core) {
+ CopyFrameInfoFrom(*(core_->get()));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/shared_desktop_frame.h b/third_party/libwebrtc/modules/desktop_capture/shared_desktop_frame.h
new file mode 100644
index 0000000000..c6f52247f4
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/shared_desktop_frame.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_SHARED_DESKTOP_FRAME_H_
+#define MODULES_DESKTOP_CAPTURE_SHARED_DESKTOP_FRAME_H_
+
+#include <memory>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "rtc_base/ref_counted_object.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// SharedDesktopFrame is a DesktopFrame that may have multiple instances all
+// sharing the same buffer.
+class RTC_EXPORT SharedDesktopFrame final : public DesktopFrame {
+ public:
+ ~SharedDesktopFrame() override;
+
+ SharedDesktopFrame(const SharedDesktopFrame&) = delete;
+ SharedDesktopFrame& operator=(const SharedDesktopFrame&) = delete;
+
+ static std::unique_ptr<SharedDesktopFrame> Wrap(
+ std::unique_ptr<DesktopFrame> desktop_frame);
+
+ // Deprecated.
+ // TODO(sergeyu): remove this method.
+ static SharedDesktopFrame* Wrap(DesktopFrame* desktop_frame);
+
+ // Deprecated. Clients do not need to know the underlying DesktopFrame
+ // instance.
+ // TODO(zijiehe): Remove this method.
+ // Returns the underlying instance of DesktopFrame.
+ DesktopFrame* GetUnderlyingFrame();
+
+ // Returns whether `this` and `other` share the underlying DesktopFrame.
+ bool ShareFrameWith(const SharedDesktopFrame& other) const;
+
+ // Creates a clone of this object.
+ std::unique_ptr<SharedDesktopFrame> Share();
+
+ // Checks if the frame is currently shared. If it returns false it's
+ // guaranteed that there are no clones of the object.
+ bool IsShared();
+
+ private:
+ typedef rtc::FinalRefCountedObject<std::unique_ptr<DesktopFrame>> Core;
+
+ SharedDesktopFrame(rtc::scoped_refptr<Core> core);
+
+ const rtc::scoped_refptr<Core> core_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_SHARED_DESKTOP_FRAME_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/shared_memory.cc b/third_party/libwebrtc/modules/desktop_capture/shared_memory.cc
new file mode 100644
index 0000000000..b4ff78b2a0
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/shared_memory.cc
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/shared_memory.h"
+
+namespace webrtc {
+
+#if defined(WEBRTC_WIN)
+const SharedMemory::Handle SharedMemory::kInvalidHandle = NULL;
+#else
+const SharedMemory::Handle SharedMemory::kInvalidHandle = -1;
+#endif
+
+SharedMemory::SharedMemory(void* data, size_t size, Handle handle, int id)
+ : data_(data), size_(size), handle_(handle), id_(id) {}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/shared_memory.h b/third_party/libwebrtc/modules/desktop_capture/shared_memory.h
new file mode 100644
index 0000000000..a7add4447b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/shared_memory.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_SHARED_MEMORY_H_
+#define MODULES_DESKTOP_CAPTURE_SHARED_MEMORY_H_
+
+#include <stddef.h>
+
+#if defined(WEBRTC_WIN)
+// Forward declare HANDLE in a windows.h compatible way so that we can avoid
+// including windows.h.
+typedef void* HANDLE;
+#endif
+
+#include <memory>
+
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// SharedMemory is a base class for shared memory. It stores all required
+// parameters of the buffer, but doesn't have any logic to allocate or destroy
+// the actual buffer. DesktopCapturer consumers that need to use shared memory
+// for video frames must extend this class with creation and destruction logic
+// specific for the target platform and then call
+// DesktopCapturer::SetSharedMemoryFactory().
+class RTC_EXPORT SharedMemory {
+ public:
+#if defined(WEBRTC_WIN)
+ typedef HANDLE Handle;
+ static const Handle kInvalidHandle;
+#else
+ typedef int Handle;
+ static const Handle kInvalidHandle;
+#endif
+
+ void* data() const { return data_; }
+ size_t size() const { return size_; }
+
+ // Platform-specific handle of the buffer.
+ Handle handle() const { return handle_; }
+
+ // Integer identifier that can be used used by consumers of DesktopCapturer
+ // interface to identify shared memory buffers it created.
+ int id() const { return id_; }
+
+ virtual ~SharedMemory() {}
+
+ SharedMemory(const SharedMemory&) = delete;
+ SharedMemory& operator=(const SharedMemory&) = delete;
+
+ protected:
+ SharedMemory(void* data, size_t size, Handle handle, int id);
+
+ void* const data_;
+ const size_t size_;
+ const Handle handle_;
+ const int id_;
+};
+
+// Interface used to create SharedMemory instances.
+class SharedMemoryFactory {
+ public:
+ SharedMemoryFactory() {}
+ virtual ~SharedMemoryFactory() {}
+
+ SharedMemoryFactory(const SharedMemoryFactory&) = delete;
+ SharedMemoryFactory& operator=(const SharedMemoryFactory&) = delete;
+
+ virtual std::unique_ptr<SharedMemory> CreateSharedMemory(size_t size) = 0;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_SHARED_MEMORY_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/test_utils.cc b/third_party/libwebrtc/modules/desktop_capture/test_utils.cc
new file mode 100644
index 0000000000..9483bf41ea
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/test_utils.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/test_utils.h"
+
+#include <stdint.h>
+#include <string.h>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+void ClearDesktopFrame(DesktopFrame* frame) {
+ RTC_DCHECK(frame);
+ uint8_t* data = frame->data();
+ for (int i = 0; i < frame->size().height(); i++) {
+ memset(data, 0, frame->size().width() * DesktopFrame::kBytesPerPixel);
+ data += frame->stride();
+ }
+}
+
+bool DesktopFrameDataEquals(const DesktopFrame& left,
+ const DesktopFrame& right) {
+ if (!left.size().equals(right.size())) {
+ return false;
+ }
+
+ const uint8_t* left_array = left.data();
+ const uint8_t* right_array = right.data();
+ for (int i = 0; i < left.size().height(); i++) {
+ if (memcmp(left_array, right_array,
+ DesktopFrame::kBytesPerPixel * left.size().width()) != 0) {
+ return false;
+ }
+ left_array += left.stride();
+ right_array += right.stride();
+ }
+
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/test_utils.h b/third_party/libwebrtc/modules/desktop_capture/test_utils.h
new file mode 100644
index 0000000000..8669fecba3
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/test_utils.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_TEST_UTILS_H_
+#define MODULES_DESKTOP_CAPTURE_TEST_UTILS_H_
+
+#include "modules/desktop_capture/desktop_frame.h"
+
+namespace webrtc {
+
+// Clears a DesktopFrame `frame` by setting its data() into 0.
+void ClearDesktopFrame(DesktopFrame* frame);
+
+// Compares size() and data() of two DesktopFrames `left` and `right`.
+bool DesktopFrameDataEquals(const DesktopFrame& left,
+ const DesktopFrame& right);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_TEST_UTILS_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/test_utils_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/test_utils_unittest.cc
new file mode 100644
index 0000000000..c1326f01cc
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/test_utils_unittest.cc
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/test_utils.h"
+
+#include <stdint.h>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/rgba_color.h"
+#include "rtc_base/checks.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+namespace {
+
+void PaintDesktopFrame(DesktopFrame* frame,
+ DesktopVector pos,
+ RgbaColor color) {
+ ASSERT_TRUE(frame);
+ ASSERT_TRUE(DesktopRect::MakeSize(frame->size()).Contains(pos));
+ *reinterpret_cast<uint32_t*>(frame->GetFrameDataAtPos(pos)) =
+ color.ToUInt32();
+}
+
+// A DesktopFrame implementation to store data in heap, but the stide is
+// doubled.
+class DoubleSizeDesktopFrame : public DesktopFrame {
+ public:
+ explicit DoubleSizeDesktopFrame(DesktopSize size);
+ ~DoubleSizeDesktopFrame() override;
+};
+
+DoubleSizeDesktopFrame::DoubleSizeDesktopFrame(DesktopSize size)
+ : DesktopFrame(
+ size,
+ kBytesPerPixel * size.width() * 2,
+ new uint8_t[kBytesPerPixel * size.width() * size.height() * 2],
+ nullptr) {}
+
+DoubleSizeDesktopFrame::~DoubleSizeDesktopFrame() {
+ delete[] data_;
+}
+
+} // namespace
+
+TEST(TestUtilsTest, BasicDataEqualsCases) {
+ BasicDesktopFrame frame(DesktopSize(4, 4));
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ PaintDesktopFrame(&frame, DesktopVector(i, j), RgbaColor(4U * j + i));
+ }
+ }
+
+ ASSERT_TRUE(DesktopFrameDataEquals(frame, frame));
+ BasicDesktopFrame other(DesktopSize(4, 4));
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ PaintDesktopFrame(&other, DesktopVector(i, j), RgbaColor(4U * j + i));
+ }
+ }
+ ASSERT_TRUE(DesktopFrameDataEquals(frame, other));
+ PaintDesktopFrame(&other, DesktopVector(2, 2), RgbaColor(0U));
+ ASSERT_FALSE(DesktopFrameDataEquals(frame, other));
+}
+
+TEST(TestUtilsTest, DifferentSizeShouldNotEqual) {
+ BasicDesktopFrame frame(DesktopSize(4, 4));
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ PaintDesktopFrame(&frame, DesktopVector(i, j), RgbaColor(4U * j + i));
+ }
+ }
+
+ BasicDesktopFrame other(DesktopSize(2, 8));
+ for (int i = 0; i < 2; i++) {
+ for (int j = 0; j < 8; j++) {
+ PaintDesktopFrame(&other, DesktopVector(i, j), RgbaColor(2U * j + i));
+ }
+ }
+
+ ASSERT_FALSE(DesktopFrameDataEquals(frame, other));
+}
+
+TEST(TestUtilsTest, DifferentStrideShouldBeComparable) {
+ BasicDesktopFrame frame(DesktopSize(4, 4));
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ PaintDesktopFrame(&frame, DesktopVector(i, j), RgbaColor(4U * j + i));
+ }
+ }
+
+ ASSERT_TRUE(DesktopFrameDataEquals(frame, frame));
+ DoubleSizeDesktopFrame other(DesktopSize(4, 4));
+ for (int i = 0; i < 4; i++) {
+ for (int j = 0; j < 4; j++) {
+ PaintDesktopFrame(&other, DesktopVector(i, j), RgbaColor(4U * j + i));
+ }
+ }
+ ASSERT_TRUE(DesktopFrameDataEquals(frame, other));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor.cc b/third_party/libwebrtc/modules/desktop_capture/win/cursor.cc
new file mode 100644
index 0000000000..1d645098e2
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor.cc
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/cursor.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/win/scoped_gdi_object.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/system/arch.h"
+
+namespace webrtc {
+
+namespace {
+
+#if defined(WEBRTC_ARCH_LITTLE_ENDIAN)
+
+#define RGBA(r, g, b, a) \
+ ((((a) << 24) & 0xff000000) | (((b) << 16) & 0xff0000) | \
+ (((g) << 8) & 0xff00) | ((r)&0xff))
+
+#else // !defined(WEBRTC_ARCH_LITTLE_ENDIAN)
+
+#define RGBA(r, g, b, a) \
+ ((((r) << 24) & 0xff000000) | (((g) << 16) & 0xff0000) | \
+ (((b) << 8) & 0xff00) | ((a)&0xff))
+
+#endif // !defined(WEBRTC_ARCH_LITTLE_ENDIAN)
+
+const int kBytesPerPixel = DesktopFrame::kBytesPerPixel;
+
+// Pixel colors used when generating cursor outlines.
+const uint32_t kPixelRgbaBlack = RGBA(0, 0, 0, 0xff);
+const uint32_t kPixelRgbaWhite = RGBA(0xff, 0xff, 0xff, 0xff);
+const uint32_t kPixelRgbaTransparent = RGBA(0, 0, 0, 0);
+
+const uint32_t kPixelRgbWhite = RGB(0xff, 0xff, 0xff);
+
+// Expands the cursor shape to add a white outline for visibility against
+// dark backgrounds.
+void AddCursorOutline(int width, int height, uint32_t* data) {
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ // If this is a transparent pixel (bgr == 0 and alpha = 0), check the
+ // neighbor pixels to see if this should be changed to an outline pixel.
+ if (*data == kPixelRgbaTransparent) {
+ // Change to white pixel if any neighbors (top, bottom, left, right)
+ // are black.
+ if ((y > 0 && data[-width] == kPixelRgbaBlack) ||
+ (y < height - 1 && data[width] == kPixelRgbaBlack) ||
+ (x > 0 && data[-1] == kPixelRgbaBlack) ||
+ (x < width - 1 && data[1] == kPixelRgbaBlack)) {
+ *data = kPixelRgbaWhite;
+ }
+ }
+ data++;
+ }
+ }
+}
+
+// Premultiplies RGB components of the pixel data in the given image by
+// the corresponding alpha components.
+void AlphaMul(uint32_t* data, int width, int height) {
+ static_assert(sizeof(uint32_t) == kBytesPerPixel,
+ "size of uint32 should be the number of bytes per pixel");
+
+ for (uint32_t* data_end = data + width * height; data != data_end; ++data) {
+ RGBQUAD* from = reinterpret_cast<RGBQUAD*>(data);
+ RGBQUAD* to = reinterpret_cast<RGBQUAD*>(data);
+ to->rgbBlue =
+ (static_cast<uint16_t>(from->rgbBlue) * from->rgbReserved) / 0xff;
+ to->rgbGreen =
+ (static_cast<uint16_t>(from->rgbGreen) * from->rgbReserved) / 0xff;
+ to->rgbRed =
+ (static_cast<uint16_t>(from->rgbRed) * from->rgbReserved) / 0xff;
+ }
+}
+
+// Scans a 32bpp bitmap looking for any pixels with non-zero alpha component.
+// Returns true if non-zero alpha is found. `stride` is expressed in pixels.
+bool HasAlphaChannel(const uint32_t* data, int stride, int width, int height) {
+ const RGBQUAD* plane = reinterpret_cast<const RGBQUAD*>(data);
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ if (plane->rgbReserved != 0)
+ return true;
+ plane += 1;
+ }
+ plane += stride - width;
+ }
+
+ return false;
+}
+
+} // namespace
+
+MouseCursor* CreateMouseCursorFromHCursor(HDC dc, HCURSOR cursor) {
+ ICONINFO iinfo;
+ if (!GetIconInfo(cursor, &iinfo)) {
+ RTC_LOG_F(LS_ERROR) << "Unable to get cursor icon info. Error = "
+ << GetLastError();
+ return NULL;
+ }
+
+ int hotspot_x = iinfo.xHotspot;
+ int hotspot_y = iinfo.yHotspot;
+
+ // Make sure the bitmaps will be freed.
+ win::ScopedBitmap scoped_mask(iinfo.hbmMask);
+ win::ScopedBitmap scoped_color(iinfo.hbmColor);
+ bool is_color = iinfo.hbmColor != NULL;
+
+ // Get `scoped_mask` dimensions.
+ BITMAP bitmap_info;
+ if (!GetObject(scoped_mask, sizeof(bitmap_info), &bitmap_info)) {
+ RTC_LOG_F(LS_ERROR) << "Unable to get bitmap info. Error = "
+ << GetLastError();
+ return NULL;
+ }
+
+ int width = bitmap_info.bmWidth;
+ int height = bitmap_info.bmHeight;
+ std::unique_ptr<uint32_t[]> mask_data(new uint32_t[width * height]);
+
+ // Get pixel data from `scoped_mask` converting it to 32bpp along the way.
+ // GetDIBits() sets the alpha component of every pixel to 0.
+ BITMAPV5HEADER bmi = {0};
+ bmi.bV5Size = sizeof(bmi);
+ bmi.bV5Width = width;
+ bmi.bV5Height = -height; // request a top-down bitmap.
+ bmi.bV5Planes = 1;
+ bmi.bV5BitCount = kBytesPerPixel * 8;
+ bmi.bV5Compression = BI_RGB;
+ bmi.bV5AlphaMask = 0xff000000;
+ bmi.bV5CSType = LCS_WINDOWS_COLOR_SPACE;
+ bmi.bV5Intent = LCS_GM_BUSINESS;
+ if (!GetDIBits(dc, scoped_mask, 0, height, mask_data.get(),
+ reinterpret_cast<BITMAPINFO*>(&bmi), DIB_RGB_COLORS)) {
+ RTC_LOG_F(LS_ERROR) << "Unable to get bitmap bits. Error = "
+ << GetLastError();
+ return NULL;
+ }
+
+ uint32_t* mask_plane = mask_data.get();
+ std::unique_ptr<DesktopFrame> image(
+ new BasicDesktopFrame(DesktopSize(width, height)));
+ bool has_alpha = false;
+
+ if (is_color) {
+ image.reset(new BasicDesktopFrame(DesktopSize(width, height)));
+ // Get the pixels from the color bitmap.
+ if (!GetDIBits(dc, scoped_color, 0, height, image->data(),
+ reinterpret_cast<BITMAPINFO*>(&bmi), DIB_RGB_COLORS)) {
+ RTC_LOG_F(LS_ERROR) << "Unable to get bitmap bits. Error = "
+ << GetLastError();
+ return NULL;
+ }
+
+ // GetDIBits() does not provide any indication whether the bitmap has alpha
+ // channel, so we use HasAlphaChannel() below to find it out.
+ has_alpha = HasAlphaChannel(reinterpret_cast<uint32_t*>(image->data()),
+ width, width, height);
+ } else {
+ // For non-color cursors, the mask contains both an AND and an XOR mask and
+ // the height includes both. Thus, the width is correct, but we need to
+ // divide by 2 to get the correct mask height.
+ height /= 2;
+
+ image.reset(new BasicDesktopFrame(DesktopSize(width, height)));
+
+ // The XOR mask becomes the color bitmap.
+ memcpy(image->data(), mask_plane + (width * height),
+ image->stride() * height);
+ }
+
+ // Reconstruct transparency from the mask if the color image does not has
+ // alpha channel.
+ if (!has_alpha) {
+ bool add_outline = false;
+ uint32_t* dst = reinterpret_cast<uint32_t*>(image->data());
+ uint32_t* mask = mask_plane;
+ for (int y = 0; y < height; y++) {
+ for (int x = 0; x < width; x++) {
+ // The two bitmaps combine as follows:
+ // mask color Windows Result Our result RGB Alpha
+ // 0 00 Black Black 00 ff
+ // 0 ff White White ff ff
+ // 1 00 Screen Transparent 00 00
+ // 1 ff Reverse-screen Black 00 ff
+ //
+ // Since we don't support XOR cursors, we replace the "Reverse Screen"
+ // with black. In this case, we also add an outline around the cursor
+ // so that it is visible against a dark background.
+ if (*mask == kPixelRgbWhite) {
+ if (*dst != 0) {
+ add_outline = true;
+ *dst = kPixelRgbaBlack;
+ } else {
+ *dst = kPixelRgbaTransparent;
+ }
+ } else {
+ *dst = kPixelRgbaBlack ^ *dst;
+ }
+
+ ++dst;
+ ++mask;
+ }
+ }
+ if (add_outline) {
+ AddCursorOutline(width, height,
+ reinterpret_cast<uint32_t*>(image->data()));
+ }
+ }
+
+ // Pre-multiply the resulting pixels since MouseCursor uses premultiplied
+ // images.
+ AlphaMul(reinterpret_cast<uint32_t*>(image->data()), width, height);
+
+ return new MouseCursor(image.release(), DesktopVector(hotspot_x, hotspot_y));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor.h b/third_party/libwebrtc/modules/desktop_capture/win/cursor.h
new file mode 100644
index 0000000000..54d78164a3
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_CURSOR_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_CURSOR_H_
+
+#include <windows.h>
+
+namespace webrtc {
+
+class MouseCursor;
+
+// Converts an HCURSOR into a `MouseCursor` instance.
+MouseCursor* CreateMouseCursorFromHCursor(HDC dc, HCURSOR cursor);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_CURSOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_24bpp.cur b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_24bpp.cur
new file mode 100644
index 0000000000..27702b825c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_24bpp.cur
Binary files differ
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_32bpp.cur b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_32bpp.cur
new file mode 100644
index 0000000000..7e0d8596da
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_32bpp.cur
Binary files differ
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_8bpp.cur b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_8bpp.cur
new file mode 100644
index 0000000000..fefb09e1a1
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/1_8bpp.cur
Binary files differ
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_1bpp.cur b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_1bpp.cur
new file mode 100644
index 0000000000..4f8a094f31
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_1bpp.cur
Binary files differ
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_32bpp.cur b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_32bpp.cur
new file mode 100644
index 0000000000..ac9cdbfbb3
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/2_32bpp.cur
Binary files differ
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_32bpp.cur b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_32bpp.cur
new file mode 100644
index 0000000000..efdbee5415
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_32bpp.cur
Binary files differ
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_4bpp.cur b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_4bpp.cur
new file mode 100644
index 0000000000..9678d55446
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_test_data/3_4bpp.cur
Binary files differ
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest.cc
new file mode 100644
index 0000000000..23f5d89571
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest.cc
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/cursor.h"
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/win/cursor_unittest_resources.h"
+#include "modules/desktop_capture/win/scoped_gdi_object.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+namespace {
+
+// Loads `left` from resources, converts it to a `MouseCursor` instance and
+// compares pixels with `right`. Returns true of MouseCursor bits match `right`.
+// `right` must be a 32bpp cursor with alpha channel.
+bool ConvertToMouseShapeAndCompare(unsigned left, unsigned right) {
+ HMODULE instance = GetModuleHandle(NULL);
+
+ // Load `left` from the EXE module's resources.
+ win::ScopedCursor cursor(reinterpret_cast<HCURSOR>(
+ LoadImage(instance, MAKEINTRESOURCE(left), IMAGE_CURSOR, 0, 0, 0)));
+ EXPECT_TRUE(cursor != NULL);
+
+ // Convert `cursor` to `mouse_shape`.
+ HDC dc = GetDC(NULL);
+ std::unique_ptr<MouseCursor> mouse_shape(
+ CreateMouseCursorFromHCursor(dc, cursor));
+ ReleaseDC(NULL, dc);
+
+ EXPECT_TRUE(mouse_shape.get());
+
+ // Load `right`.
+ cursor.Set(reinterpret_cast<HCURSOR>(
+ LoadImage(instance, MAKEINTRESOURCE(right), IMAGE_CURSOR, 0, 0, 0)));
+
+ ICONINFO iinfo;
+ EXPECT_TRUE(GetIconInfo(cursor, &iinfo));
+ EXPECT_TRUE(iinfo.hbmColor);
+
+ // Make sure the bitmaps will be freed.
+ win::ScopedBitmap scoped_mask(iinfo.hbmMask);
+ win::ScopedBitmap scoped_color(iinfo.hbmColor);
+
+ // Get `scoped_color` dimensions.
+ BITMAP bitmap_info;
+ EXPECT_TRUE(GetObject(scoped_color, sizeof(bitmap_info), &bitmap_info));
+
+ int width = bitmap_info.bmWidth;
+ int height = bitmap_info.bmHeight;
+ EXPECT_TRUE(DesktopSize(width, height).equals(mouse_shape->image()->size()));
+
+ // Get the pixels from `scoped_color`.
+ int size = width * height;
+ std::unique_ptr<uint32_t[]> data(new uint32_t[size]);
+ EXPECT_TRUE(GetBitmapBits(scoped_color, size * sizeof(uint32_t), data.get()));
+
+ // Compare the 32bpp image in `mouse_shape` with the one loaded from `right`.
+ return memcmp(data.get(), mouse_shape->image()->data(),
+ size * sizeof(uint32_t)) == 0;
+}
+
+} // namespace
+
+TEST(MouseCursorTest, MatchCursors) {
+ EXPECT_TRUE(
+ ConvertToMouseShapeAndCompare(IDD_CURSOR1_24BPP, IDD_CURSOR1_32BPP));
+
+ EXPECT_TRUE(
+ ConvertToMouseShapeAndCompare(IDD_CURSOR1_8BPP, IDD_CURSOR1_32BPP));
+
+ EXPECT_TRUE(
+ ConvertToMouseShapeAndCompare(IDD_CURSOR2_1BPP, IDD_CURSOR2_32BPP));
+
+ EXPECT_TRUE(
+ ConvertToMouseShapeAndCompare(IDD_CURSOR3_4BPP, IDD_CURSOR3_32BPP));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.h b/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.h
new file mode 100644
index 0000000000..f583554d68
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_CURSOR_UNITTEST_RESOURCES_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_CURSOR_UNITTEST_RESOURCES_H_
+
+#define IDD_CURSOR1_24BPP 101
+#define IDD_CURSOR1_32BPP 102
+#define IDD_CURSOR1_8BPP 103
+
+#define IDD_CURSOR2_1BPP 104
+#define IDD_CURSOR2_32BPP 105
+
+#define IDD_CURSOR3_4BPP 106
+#define IDD_CURSOR3_32BPP 107
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_CURSOR_UNITTEST_RESOURCES_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.rc b/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.rc
new file mode 100644
index 0000000000..90073791c9
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/cursor_unittest_resources.rc
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/cursor_unittest_resources.h"
+
+// These cursors are matched with their less than 32bpp counterparts below.
+IDD_CURSOR1_32BPP CURSOR "cursor_test_data/1_32bpp.cur"
+IDD_CURSOR2_32BPP CURSOR "cursor_test_data/2_32bpp.cur"
+IDD_CURSOR3_32BPP CURSOR "cursor_test_data/3_32bpp.cur"
+
+// Matches IDD_CURSOR1_32BPP.
+IDD_CURSOR1_24BPP CURSOR "cursor_test_data/1_24bpp.cur"
+
+// Matches IDD_CURSOR1_32BPP.
+IDD_CURSOR1_8BPP CURSOR "cursor_test_data/1_8bpp.cur"
+
+// Matches IDD_CURSOR2_32BPP.
+IDD_CURSOR2_1BPP CURSOR "cursor_test_data/2_1bpp.cur"
+
+// Matches IDD_CURSOR3_32BPP.
+IDD_CURSOR3_4BPP CURSOR "cursor_test_data/3_4bpp.cur"
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/d3d_device.cc b/third_party/libwebrtc/modules/desktop_capture/win/d3d_device.cc
new file mode 100644
index 0000000000..3d46117501
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/d3d_device.cc
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/d3d_device.h"
+
+#include <utility>
+
+#include "modules/desktop_capture/win/desktop_capture_utils.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+using Microsoft::WRL::ComPtr;
+
+D3dDevice::D3dDevice() = default;
+D3dDevice::D3dDevice(const D3dDevice& other) = default;
+D3dDevice::D3dDevice(D3dDevice&& other) = default;
+D3dDevice::~D3dDevice() = default;
+
+bool D3dDevice::Initialize(const ComPtr<IDXGIAdapter>& adapter) {
+ dxgi_adapter_ = adapter;
+ if (!dxgi_adapter_) {
+ RTC_LOG(LS_WARNING) << "An empty IDXGIAdapter instance has been received.";
+ return false;
+ }
+
+ D3D_FEATURE_LEVEL feature_level;
+ // Default feature levels contain D3D 9.1 through D3D 11.0.
+ _com_error error = D3D11CreateDevice(
+ adapter.Get(), D3D_DRIVER_TYPE_UNKNOWN, nullptr,
+ D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_SINGLETHREADED,
+ nullptr, 0, D3D11_SDK_VERSION, d3d_device_.GetAddressOf(), &feature_level,
+ context_.GetAddressOf());
+ if (error.Error() != S_OK || !d3d_device_ || !context_) {
+ RTC_LOG(LS_WARNING) << "D3D11CreateDevice returned: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ if (feature_level < D3D_FEATURE_LEVEL_11_0) {
+ RTC_LOG(LS_WARNING)
+ << "D3D11CreateDevice returned an instance without DirectX 11 support, "
+ << "level " << feature_level << ". Following initialization may fail.";
+ // D3D_FEATURE_LEVEL_11_0 is not officially documented on MSDN to be a
+ // requirement of Dxgi duplicator APIs.
+ }
+
+ error = d3d_device_.As(&dxgi_device_);
+ if (error.Error() != S_OK || !dxgi_device_) {
+ RTC_LOG(LS_WARNING)
+ << "ID3D11Device is not an implementation of IDXGIDevice, "
+ << "this usually means the system does not support DirectX "
+ << "11. Error received: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ return true;
+}
+
+// static
+std::vector<D3dDevice> D3dDevice::EnumDevices() {
+ ComPtr<IDXGIFactory1> factory;
+ _com_error error =
+ CreateDXGIFactory1(__uuidof(IDXGIFactory1),
+ reinterpret_cast<void**>(factory.GetAddressOf()));
+ if (error.Error() != S_OK || !factory) {
+ RTC_LOG(LS_WARNING) << "Cannot create IDXGIFactory1: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return std::vector<D3dDevice>();
+ }
+
+ std::vector<D3dDevice> result;
+ for (int i = 0;; i++) {
+ ComPtr<IDXGIAdapter> adapter;
+ error = factory->EnumAdapters(i, adapter.GetAddressOf());
+ if (error.Error() == S_OK) {
+ D3dDevice device;
+ if (device.Initialize(adapter)) {
+ result.push_back(std::move(device));
+ }
+ } else if (error.Error() == DXGI_ERROR_NOT_FOUND) {
+ break;
+ } else {
+ RTC_LOG(LS_WARNING)
+ << "IDXGIFactory1::EnumAdapters returned an unexpected error: "
+ << desktop_capture::utils::ComErrorToString(error);
+ }
+ }
+ return result;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/d3d_device.h b/third_party/libwebrtc/modules/desktop_capture/win/d3d_device.h
new file mode 100644
index 0000000000..aeb9d1823a
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/d3d_device.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_D3D_DEVICE_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_D3D_DEVICE_H_
+
+#include <comdef.h>
+#include <d3d11.h>
+#include <dxgi.h>
+#include <wrl/client.h>
+
+#include <vector>
+
+namespace webrtc {
+
+// A wrapper of ID3D11Device and its corresponding context and IDXGIAdapter.
+// This class represents one video card in the system.
+class D3dDevice {
+ public:
+ D3dDevice(const D3dDevice& other);
+ D3dDevice(D3dDevice&& other);
+ ~D3dDevice();
+
+ ID3D11Device* d3d_device() const { return d3d_device_.Get(); }
+
+ ID3D11DeviceContext* context() const { return context_.Get(); }
+
+ IDXGIDevice* dxgi_device() const { return dxgi_device_.Get(); }
+
+ IDXGIAdapter* dxgi_adapter() const { return dxgi_adapter_.Get(); }
+
+ // Returns all D3dDevice instances on the system. Returns an empty vector if
+ // anything wrong.
+ static std::vector<D3dDevice> EnumDevices();
+
+ private:
+ // Instances of D3dDevice should only be created by EnumDevices() static
+ // function.
+ D3dDevice();
+
+ // Initializes the D3dDevice from an IDXGIAdapter.
+ bool Initialize(const Microsoft::WRL::ComPtr<IDXGIAdapter>& adapter);
+
+ Microsoft::WRL::ComPtr<ID3D11Device> d3d_device_;
+ Microsoft::WRL::ComPtr<ID3D11DeviceContext> context_;
+ Microsoft::WRL::ComPtr<IDXGIDevice> dxgi_device_;
+ Microsoft::WRL::ComPtr<IDXGIAdapter> dxgi_adapter_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_D3D_DEVICE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/desktop.cc b/third_party/libwebrtc/modules/desktop_capture/win/desktop.cc
new file mode 100644
index 0000000000..4a671dd9ae
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/desktop.cc
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/desktop.h"
+
+#include <vector>
+
+#include "rtc_base/logging.h"
+#include "rtc_base/string_utils.h"
+
+namespace webrtc {
+
+Desktop::Desktop(HDESK desktop, bool own) : desktop_(desktop), own_(own) {}
+
+Desktop::~Desktop() {
+ if (own_ && desktop_ != NULL) {
+ if (!::CloseDesktop(desktop_)) {
+ RTC_LOG(LS_ERROR) << "Failed to close the owned desktop handle: "
+ << GetLastError();
+ }
+ }
+}
+
+bool Desktop::GetName(std::wstring* desktop_name_out) const {
+ if (desktop_ == NULL)
+ return false;
+
+ DWORD length = 0;
+ int rv = GetUserObjectInformationW(desktop_, UOI_NAME, NULL, 0, &length);
+ if (rv || GetLastError() != ERROR_INSUFFICIENT_BUFFER)
+ abort();
+
+ length /= sizeof(WCHAR);
+ std::vector<WCHAR> buffer(length);
+ if (!GetUserObjectInformationW(desktop_, UOI_NAME, &buffer[0],
+ length * sizeof(WCHAR), &length)) {
+ RTC_LOG(LS_ERROR) << "Failed to query the desktop name: " << GetLastError();
+ return false;
+ }
+
+ desktop_name_out->assign(&buffer[0], length / sizeof(WCHAR));
+ return true;
+}
+
+bool Desktop::IsSame(const Desktop& other) const {
+ std::wstring name;
+ if (!GetName(&name))
+ return false;
+
+ std::wstring other_name;
+ if (!other.GetName(&other_name))
+ return false;
+
+ return name == other_name;
+}
+
+bool Desktop::SetThreadDesktop() const {
+ if (!::SetThreadDesktop(desktop_)) {
+ RTC_LOG(LS_ERROR) << "Failed to assign the desktop to the current thread: "
+ << GetLastError();
+ return false;
+ }
+
+ return true;
+}
+
+Desktop* Desktop::GetDesktop(const WCHAR* desktop_name) {
+ ACCESS_MASK desired_access = DESKTOP_CREATEMENU | DESKTOP_CREATEWINDOW |
+ DESKTOP_ENUMERATE | DESKTOP_HOOKCONTROL |
+ DESKTOP_WRITEOBJECTS | DESKTOP_READOBJECTS |
+ DESKTOP_SWITCHDESKTOP | GENERIC_WRITE;
+ HDESK desktop = OpenDesktopW(desktop_name, 0, FALSE, desired_access);
+ if (desktop == NULL) {
+ RTC_LOG(LS_ERROR) << "Failed to open the desktop '"
+ << rtc::ToUtf8(desktop_name) << "': " << GetLastError();
+ return NULL;
+ }
+
+ return new Desktop(desktop, true);
+}
+
+Desktop* Desktop::GetInputDesktop() {
+ HDESK desktop = OpenInputDesktop(
+ 0, FALSE, GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE);
+ if (desktop == NULL)
+ return NULL;
+
+ return new Desktop(desktop, true);
+}
+
+Desktop* Desktop::GetThreadDesktop() {
+ HDESK desktop = ::GetThreadDesktop(GetCurrentThreadId());
+ if (desktop == NULL) {
+ RTC_LOG(LS_ERROR)
+ << "Failed to retrieve the handle of the desktop assigned to "
+ "the current thread: "
+ << GetLastError();
+ return NULL;
+ }
+
+ return new Desktop(desktop, false);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/desktop.h b/third_party/libwebrtc/modules/desktop_capture/win/desktop.h
new file mode 100644
index 0000000000..01bed8592d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/desktop.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_H_
+
+#include <windows.h>
+
+#include <string>
+
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+class RTC_EXPORT Desktop {
+ public:
+ ~Desktop();
+
+ Desktop(const Desktop&) = delete;
+ Desktop& operator=(const Desktop&) = delete;
+
+ // Returns the name of the desktop represented by the object. Return false if
+ // quering the name failed for any reason.
+ bool GetName(std::wstring* desktop_name_out) const;
+
+ // Returns true if `other` has the same name as this desktop. Returns false
+ // in any other case including failing Win32 APIs and uninitialized desktop
+ // handles.
+ bool IsSame(const Desktop& other) const;
+
+ // Assigns the desktop to the current thread. Returns false is the operation
+ // failed for any reason.
+ bool SetThreadDesktop() const;
+
+ // Returns the desktop by its name or NULL if an error occurs.
+ static Desktop* GetDesktop(const wchar_t* desktop_name);
+
+ // Returns the desktop currently receiving user input or NULL if an error
+ // occurs.
+ static Desktop* GetInputDesktop();
+
+ // Returns the desktop currently assigned to the calling thread or NULL if
+ // an error occurs.
+ static Desktop* GetThreadDesktop();
+
+ private:
+ Desktop(HDESK desktop, bool own);
+
+ // The desktop handle.
+ HDESK desktop_;
+
+ // True if `desktop_` must be closed on teardown.
+ bool own_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.cc b/third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.cc
new file mode 100644
index 0000000000..476ddc4aba
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.cc
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/desktop_capture_utils.h"
+
+#include "rtc_base/strings/string_builder.h"
+
+namespace webrtc {
+namespace desktop_capture {
+namespace utils {
+
+// Generates a human-readable string from a COM error.
+std::string ComErrorToString(const _com_error& error) {
+ char buffer[1024];
+ rtc::SimpleStringBuilder string_builder(buffer);
+ // Use _bstr_t to simplify the wchar to char conversion for ErrorMessage().
+ _bstr_t error_message(error.ErrorMessage());
+ string_builder.AppendFormat("HRESULT: 0x%08X, Message: %s", error.Error(),
+ static_cast<const char*>(error_message));
+ return string_builder.str();
+}
+
+} // namespace utils
+} // namespace desktop_capture
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.h b/third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.h
new file mode 100644
index 0000000000..ebf31419ce
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/desktop_capture_utils.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_CAPTURE_UTILS_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_CAPTURE_UTILS_H_
+
+#include <comdef.h>
+
+#include <string>
+
+namespace webrtc {
+namespace desktop_capture {
+namespace utils {
+
+// Generates a human-readable string from a COM error.
+std::string ComErrorToString(const _com_error& error);
+
+} // namespace utils
+} // namespace desktop_capture
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DESKTOP_CAPTURE_UTILS_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.cc b/third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.cc
new file mode 100644
index 0000000000..52d89214d8
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.cc
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/display_configuration_monitor.h"
+
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+
+namespace webrtc {
+
+bool DisplayConfigurationMonitor::IsChanged() {
+ DesktopRect rect = GetFullscreenRect();
+ if (!initialized_) {
+ initialized_ = true;
+ rect_ = rect;
+ return false;
+ }
+
+ if (rect.equals(rect_)) {
+ return false;
+ }
+
+ rect_ = rect;
+ return true;
+}
+
+void DisplayConfigurationMonitor::Reset() {
+ initialized_ = false;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.h b/third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.h
new file mode 100644
index 0000000000..39c211cfbe
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/display_configuration_monitor.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_DISPLAY_CONFIGURATION_MONITOR_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DISPLAY_CONFIGURATION_MONITOR_H_
+
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+// A passive monitor to detect the change of display configuration on a Windows
+// system.
+// TODO(zijiehe): Also check for pixel format changes.
+class DisplayConfigurationMonitor {
+ public:
+ // Checks whether the change of display configuration has happened after last
+ // IsChanged() call. This function won't return true for the first time after
+ // constructor or Reset() call.
+ bool IsChanged();
+
+ // Resets to the initial state.
+ void Reset();
+
+ private:
+ DesktopRect rect_;
+ bool initialized_ = false;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DISPLAY_CONFIGURATION_MONITOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
new file mode 100644
index 0000000000..88ec4e25bf
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.cc
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/dxgi_adapter_duplicator.h"
+
+#include <comdef.h>
+#include <dxgi.h>
+
+#include <algorithm>
+
+#include "modules/desktop_capture/win/desktop_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+using Microsoft::WRL::ComPtr;
+
+namespace {
+
+bool IsValidRect(const RECT& rect) {
+ return rect.right > rect.left && rect.bottom > rect.top;
+}
+
+} // namespace
+
+DxgiAdapterDuplicator::DxgiAdapterDuplicator(const D3dDevice& device)
+ : device_(device) {}
+DxgiAdapterDuplicator::DxgiAdapterDuplicator(DxgiAdapterDuplicator&&) = default;
+DxgiAdapterDuplicator::~DxgiAdapterDuplicator() = default;
+
+bool DxgiAdapterDuplicator::Initialize() {
+ if (DoInitialize()) {
+ return true;
+ }
+ duplicators_.clear();
+ return false;
+}
+
+bool DxgiAdapterDuplicator::DoInitialize() {
+ for (int i = 0;; i++) {
+ ComPtr<IDXGIOutput> output;
+ _com_error error =
+ device_.dxgi_adapter()->EnumOutputs(i, output.GetAddressOf());
+ if (error.Error() == DXGI_ERROR_NOT_FOUND) {
+ break;
+ }
+
+ if (error.Error() == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE) {
+ RTC_LOG(LS_WARNING) << "IDXGIAdapter::EnumOutputs returned "
+ << "NOT_CURRENTLY_AVAILABLE. This may happen when "
+ << "running in session 0.";
+ break;
+ }
+
+ if (error.Error() != S_OK || !output) {
+ RTC_LOG(LS_WARNING) << "IDXGIAdapter::EnumOutputs returned an unexpected "
+ << "result: "
+ << desktop_capture::utils::ComErrorToString(error);
+ continue;
+ }
+
+ DXGI_OUTPUT_DESC desc;
+ error = output->GetDesc(&desc);
+ if (error.Error() == S_OK) {
+ if (desc.AttachedToDesktop && IsValidRect(desc.DesktopCoordinates)) {
+ ComPtr<IDXGIOutput1> output1;
+ error = output.As(&output1);
+ if (error.Error() != S_OK || !output1) {
+ RTC_LOG(LS_WARNING)
+ << "Failed to convert IDXGIOutput to IDXGIOutput1, this usually "
+ << "means the system does not support DirectX 11";
+ continue;
+ }
+ DxgiOutputDuplicator duplicator(device_, output1, desc);
+ if (!duplicator.Initialize()) {
+ RTC_LOG(LS_WARNING) << "Failed to initialize DxgiOutputDuplicator on "
+ << "output " << i;
+ continue;
+ }
+
+ duplicators_.push_back(std::move(duplicator));
+ desktop_rect_.UnionWith(duplicators_.back().desktop_rect());
+ } else {
+ RTC_LOG(LS_ERROR) << (desc.AttachedToDesktop ? "Attached" : "Detached")
+ << " output " << i << " ("
+ << desc.DesktopCoordinates.top << ", "
+ << desc.DesktopCoordinates.left << ") - ("
+ << desc.DesktopCoordinates.bottom << ", "
+ << desc.DesktopCoordinates.right << ") is ignored.";
+ }
+ } else {
+ RTC_LOG(LS_WARNING) << "Failed to get output description of device " << i
+ << ", ignore.";
+ }
+ }
+
+ if (duplicators_.empty()) {
+ RTC_LOG(LS_WARNING)
+ << "Cannot initialize any DxgiOutputDuplicator instance.";
+ }
+
+ return !duplicators_.empty();
+}
+
+void DxgiAdapterDuplicator::Setup(Context* context) {
+ RTC_DCHECK(context->contexts.empty());
+ context->contexts.resize(duplicators_.size());
+ for (size_t i = 0; i < duplicators_.size(); i++) {
+ duplicators_[i].Setup(&context->contexts[i]);
+ }
+}
+
+void DxgiAdapterDuplicator::Unregister(const Context* const context) {
+ RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size());
+ for (size_t i = 0; i < duplicators_.size(); i++) {
+ duplicators_[i].Unregister(&context->contexts[i]);
+ }
+}
+
+bool DxgiAdapterDuplicator::Duplicate(Context* context,
+ SharedDesktopFrame* target) {
+ RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size());
+ for (size_t i = 0; i < duplicators_.size(); i++) {
+ if (!duplicators_[i].Duplicate(&context->contexts[i],
+ duplicators_[i].desktop_rect().top_left(),
+ target)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DxgiAdapterDuplicator::DuplicateMonitor(Context* context,
+ int monitor_id,
+ SharedDesktopFrame* target) {
+ RTC_DCHECK_GE(monitor_id, 0);
+ RTC_DCHECK_LT(monitor_id, duplicators_.size());
+ RTC_DCHECK_EQ(context->contexts.size(), duplicators_.size());
+ return duplicators_[monitor_id].Duplicate(&context->contexts[monitor_id],
+ DesktopVector(), target);
+}
+
+DesktopRect DxgiAdapterDuplicator::ScreenRect(int id) const {
+ RTC_DCHECK_GE(id, 0);
+ RTC_DCHECK_LT(id, duplicators_.size());
+ return duplicators_[id].desktop_rect();
+}
+
+const std::string& DxgiAdapterDuplicator::GetDeviceName(int id) const {
+ RTC_DCHECK_GE(id, 0);
+ RTC_DCHECK_LT(id, duplicators_.size());
+ return duplicators_[id].device_name();
+}
+
+int DxgiAdapterDuplicator::screen_count() const {
+ return static_cast<int>(duplicators_.size());
+}
+
+int64_t DxgiAdapterDuplicator::GetNumFramesCaptured() const {
+ int64_t min = INT64_MAX;
+ for (const auto& duplicator : duplicators_) {
+ min = std::min(min, duplicator.num_frames_captured());
+ }
+
+ return min;
+}
+
+void DxgiAdapterDuplicator::TranslateRect(const DesktopVector& position) {
+ desktop_rect_.Translate(position);
+ RTC_DCHECK_GE(desktop_rect_.left(), 0);
+ RTC_DCHECK_GE(desktop_rect_.top(), 0);
+ for (auto& duplicator : duplicators_) {
+ duplicator.TranslateRect(position);
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h
new file mode 100644
index 0000000000..5931b51f9e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_adapter_duplicator.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_DXGI_ADAPTER_DUPLICATOR_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_ADAPTER_DUPLICATOR_H_
+
+#include <wrl/client.h>
+
+#include <vector>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "modules/desktop_capture/win/d3d_device.h"
+#include "modules/desktop_capture/win/dxgi_context.h"
+#include "modules/desktop_capture/win/dxgi_output_duplicator.h"
+
+namespace webrtc {
+
+// A container of DxgiOutputDuplicators to duplicate monitors attached to a
+// single video card.
+class DxgiAdapterDuplicator {
+ public:
+ using Context = DxgiAdapterContext;
+
+ // Creates an instance of DxgiAdapterDuplicator from a D3dDevice. Only
+ // DxgiDuplicatorController can create an instance.
+ explicit DxgiAdapterDuplicator(const D3dDevice& device);
+
+ // Move constructor, to make it possible to store instances of
+ // DxgiAdapterDuplicator in std::vector<>.
+ DxgiAdapterDuplicator(DxgiAdapterDuplicator&& other);
+
+ ~DxgiAdapterDuplicator();
+
+ // Initializes the DxgiAdapterDuplicator from a D3dDevice.
+ bool Initialize();
+
+ // Sequentially calls Duplicate function of all the DxgiOutputDuplicator
+ // instances owned by this instance, and writes into `target`.
+ bool Duplicate(Context* context, SharedDesktopFrame* target);
+
+ // Captures one monitor and writes into `target`. `monitor_id` should be
+ // between [0, screen_count()).
+ bool DuplicateMonitor(Context* context,
+ int monitor_id,
+ SharedDesktopFrame* target);
+
+ // Returns desktop rect covered by this DxgiAdapterDuplicator.
+ DesktopRect desktop_rect() const { return desktop_rect_; }
+
+ // Returns the size of one screen owned by this DxgiAdapterDuplicator. `id`
+ // should be between [0, screen_count()).
+ DesktopRect ScreenRect(int id) const;
+
+ // Returns the device name of one screen owned by this DxgiAdapterDuplicator
+ // in utf8 encoding. `id` should be between [0, screen_count()).
+ const std::string& GetDeviceName(int id) const;
+
+ // Returns the count of screens owned by this DxgiAdapterDuplicator. These
+ // screens can be retrieved by an interger in the range of
+ // [0, screen_count()).
+ int screen_count() const;
+
+ void Setup(Context* context);
+
+ void Unregister(const Context* const context);
+
+ // The minimum num_frames_captured() returned by `duplicators_`.
+ int64_t GetNumFramesCaptured() const;
+
+ // Moves `desktop_rect_` and all underlying `duplicators_`. See
+ // DxgiDuplicatorController::TranslateRect().
+ void TranslateRect(const DesktopVector& position);
+
+ private:
+ bool DoInitialize();
+
+ const D3dDevice device_;
+ std::vector<DxgiOutputDuplicator> duplicators_;
+ DesktopRect desktop_rect_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_ADAPTER_DUPLICATOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.cc
new file mode 100644
index 0000000000..c18b238f03
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.cc
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/dxgi_context.h"
+
+#include "modules/desktop_capture/win/dxgi_duplicator_controller.h"
+
+namespace webrtc {
+
+DxgiAdapterContext::DxgiAdapterContext() = default;
+DxgiAdapterContext::DxgiAdapterContext(const DxgiAdapterContext& context) =
+ default;
+DxgiAdapterContext::~DxgiAdapterContext() = default;
+
+DxgiFrameContext::DxgiFrameContext() = default;
+
+DxgiFrameContext::~DxgiFrameContext() {
+ Reset();
+}
+
+void DxgiFrameContext::Reset() {
+ DxgiDuplicatorController::Instance()->Unregister(this);
+ controller_id = 0;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.h
new file mode 100644
index 0000000000..59c2112db5
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_context.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_DXGI_CONTEXT_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_CONTEXT_H_
+
+#include <vector>
+
+#include "modules/desktop_capture/desktop_region.h"
+
+namespace webrtc {
+
+// A DxgiOutputContext stores the status of a single DxgiFrame of
+// DxgiOutputDuplicator.
+struct DxgiOutputContext final {
+ // The updated region DxgiOutputDuplicator::DetectUpdatedRegion() output
+ // during last Duplicate() function call. It's always relative to the (0, 0).
+ DesktopRegion updated_region;
+};
+
+// A DxgiAdapterContext stores the status of a single DxgiFrame of
+// DxgiAdapterDuplicator.
+struct DxgiAdapterContext final {
+ DxgiAdapterContext();
+ DxgiAdapterContext(const DxgiAdapterContext& other);
+ ~DxgiAdapterContext();
+
+ // Child DxgiOutputContext belongs to this AdapterContext.
+ std::vector<DxgiOutputContext> contexts;
+};
+
+// A DxgiFrameContext stores the status of a single DxgiFrame of
+// DxgiDuplicatorController.
+struct DxgiFrameContext final {
+ public:
+ DxgiFrameContext();
+ // Unregister this Context instance from DxgiDuplicatorController during
+ // destructing.
+ ~DxgiFrameContext();
+
+ // Reset current Context, so it will be reinitialized next time.
+ void Reset();
+
+ // A Context will have an exactly same `controller_id` as
+ // DxgiDuplicatorController, to ensure it has been correctly setted up after
+ // each DxgiDuplicatorController::Initialize().
+ int controller_id = 0;
+
+ // Child DxgiAdapterContext belongs to this DxgiFrameContext.
+ std::vector<DxgiAdapterContext> contexts;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_CONTEXT_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc
new file mode 100644
index 0000000000..a776896f6c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.cc
@@ -0,0 +1,514 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/dxgi_duplicator_controller.h"
+
+#include <windows.h>
+
+#include <algorithm>
+#include <string>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/win/dxgi_frame.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/time_utils.h"
+#include "system_wrappers/include/sleep.h"
+
+namespace webrtc {
+
+namespace {
+
+constexpr DWORD kInvalidSessionId = 0xFFFFFFFF;
+
+DWORD GetCurrentSessionId() {
+ DWORD session_id = kInvalidSessionId;
+ if (!::ProcessIdToSessionId(::GetCurrentProcessId(), &session_id)) {
+ RTC_LOG(LS_WARNING)
+ << "Failed to retrieve current session Id, current binary "
+ "may not have required priviledge.";
+ }
+ return session_id;
+}
+
+bool IsConsoleSession() {
+ return WTSGetActiveConsoleSessionId() == GetCurrentSessionId();
+}
+
+} // namespace
+
+// static
+std::string DxgiDuplicatorController::ResultName(
+ DxgiDuplicatorController::Result result) {
+ switch (result) {
+ case Result::SUCCEEDED:
+ return "Succeeded";
+ case Result::UNSUPPORTED_SESSION:
+ return "Unsupported session";
+ case Result::FRAME_PREPARE_FAILED:
+ return "Frame preparation failed";
+ case Result::INITIALIZATION_FAILED:
+ return "Initialization failed";
+ case Result::DUPLICATION_FAILED:
+ return "Duplication failed";
+ case Result::INVALID_MONITOR_ID:
+ return "Invalid monitor id";
+ default:
+ return "Unknown error";
+ }
+}
+
+// static
+rtc::scoped_refptr<DxgiDuplicatorController>
+DxgiDuplicatorController::Instance() {
+ // The static instance won't be deleted to ensure it can be used by other
+ // threads even during program exiting.
+ static DxgiDuplicatorController* instance = new DxgiDuplicatorController();
+ return rtc::scoped_refptr<DxgiDuplicatorController>(instance);
+}
+
+// static
+bool DxgiDuplicatorController::IsCurrentSessionSupported() {
+ DWORD current_session_id = GetCurrentSessionId();
+ return current_session_id != kInvalidSessionId && current_session_id != 0;
+}
+
+DxgiDuplicatorController::DxgiDuplicatorController() : refcount_(0) {}
+
+void DxgiDuplicatorController::AddRef() {
+ int refcount = (++refcount_);
+ RTC_DCHECK(refcount > 0);
+}
+
+void DxgiDuplicatorController::Release() {
+ int refcount = (--refcount_);
+ RTC_DCHECK(refcount >= 0);
+ if (refcount == 0) {
+ RTC_LOG(LS_WARNING) << "Count of references reaches zero, "
+ "DxgiDuplicatorController will be unloaded.";
+ Unload();
+ }
+}
+
+bool DxgiDuplicatorController::IsSupported() {
+ MutexLock lock(&mutex_);
+ return Initialize();
+}
+
+bool DxgiDuplicatorController::RetrieveD3dInfo(D3dInfo* info) {
+ bool result = false;
+ {
+ MutexLock lock(&mutex_);
+ result = Initialize();
+ *info = d3d_info_;
+ }
+ if (!result) {
+ RTC_LOG(LS_WARNING) << "Failed to initialize DXGI components, the D3dInfo "
+ "retrieved may not accurate or out of date.";
+ }
+ return result;
+}
+
+DxgiDuplicatorController::Result DxgiDuplicatorController::Duplicate(
+ DxgiFrame* frame) {
+ return DoDuplicate(frame, -1);
+}
+
+DxgiDuplicatorController::Result DxgiDuplicatorController::DuplicateMonitor(
+ DxgiFrame* frame,
+ int monitor_id) {
+ RTC_DCHECK_GE(monitor_id, 0);
+ return DoDuplicate(frame, monitor_id);
+}
+
+DesktopVector DxgiDuplicatorController::dpi() {
+ MutexLock lock(&mutex_);
+ if (Initialize()) {
+ return dpi_;
+ }
+ return DesktopVector();
+}
+
+int DxgiDuplicatorController::ScreenCount() {
+ MutexLock lock(&mutex_);
+ if (Initialize()) {
+ return ScreenCountUnlocked();
+ }
+ return 0;
+}
+
+bool DxgiDuplicatorController::GetDeviceNames(
+ std::vector<std::string>* output) {
+ MutexLock lock(&mutex_);
+ if (Initialize()) {
+ GetDeviceNamesUnlocked(output);
+ return true;
+ }
+ return false;
+}
+
+DxgiDuplicatorController::Result DxgiDuplicatorController::DoDuplicate(
+ DxgiFrame* frame,
+ int monitor_id) {
+ RTC_DCHECK(frame);
+ MutexLock lock(&mutex_);
+
+ // The dxgi components and APIs do not update the screen resolution without
+ // a reinitialization. So we use the GetDC() function to retrieve the screen
+ // resolution to decide whether dxgi components need to be reinitialized.
+ // If the screen resolution changed, it's very likely the next Duplicate()
+ // function call will fail because of a missing monitor or the frame size is
+ // not enough to store the output. So we reinitialize dxgi components in-place
+ // to avoid a capture failure.
+ // But there is no guarantee GetDC() function returns the same resolution as
+ // dxgi APIs, we still rely on dxgi components to return the output frame
+ // size.
+ // TODO(zijiehe): Confirm whether IDXGIOutput::GetDesc() and
+ // IDXGIOutputDuplication::GetDesc() can detect the resolution change without
+ // reinitialization.
+ if (display_configuration_monitor_.IsChanged()) {
+ Deinitialize();
+ }
+
+ if (!Initialize()) {
+ if (succeeded_duplications_ == 0 && !IsCurrentSessionSupported()) {
+ RTC_LOG(LS_WARNING) << "Current binary is running in session 0. DXGI "
+ "components cannot be initialized.";
+ return Result::UNSUPPORTED_SESSION;
+ }
+
+ // Cannot initialize COM components now, display mode may be changing.
+ return Result::INITIALIZATION_FAILED;
+ }
+
+ if (!frame->Prepare(SelectedDesktopSize(monitor_id), monitor_id)) {
+ return Result::FRAME_PREPARE_FAILED;
+ }
+
+ frame->frame()->mutable_updated_region()->Clear();
+
+ if (DoDuplicateUnlocked(frame->context(), monitor_id, frame->frame())) {
+ succeeded_duplications_++;
+ return Result::SUCCEEDED;
+ }
+ if (monitor_id >= ScreenCountUnlocked()) {
+ // It's a user error to provide a `monitor_id` larger than screen count. We
+ // do not need to deinitialize.
+ return Result::INVALID_MONITOR_ID;
+ }
+
+ // If the `monitor_id` is valid, but DoDuplicateUnlocked() failed, something
+ // must be wrong from capturer APIs. We should Deinitialize().
+ Deinitialize();
+ return Result::DUPLICATION_FAILED;
+}
+
+void DxgiDuplicatorController::Unload() {
+ MutexLock lock(&mutex_);
+ Deinitialize();
+}
+
+void DxgiDuplicatorController::Unregister(const Context* const context) {
+ MutexLock lock(&mutex_);
+ if (ContextExpired(context)) {
+ // The Context has not been setup after a recent initialization, so it
+ // should not been registered in duplicators.
+ return;
+ }
+ for (size_t i = 0; i < duplicators_.size(); i++) {
+ duplicators_[i].Unregister(&context->contexts[i]);
+ }
+}
+
+bool DxgiDuplicatorController::Initialize() {
+ if (!duplicators_.empty()) {
+ return true;
+ }
+
+ if (DoInitialize()) {
+ return true;
+ }
+ Deinitialize();
+ return false;
+}
+
+bool DxgiDuplicatorController::DoInitialize() {
+ RTC_DCHECK(desktop_rect_.is_empty());
+ RTC_DCHECK(duplicators_.empty());
+
+ d3d_info_.min_feature_level = static_cast<D3D_FEATURE_LEVEL>(0);
+ d3d_info_.max_feature_level = static_cast<D3D_FEATURE_LEVEL>(0);
+
+ std::vector<D3dDevice> devices = D3dDevice::EnumDevices();
+ if (devices.empty()) {
+ RTC_LOG(LS_WARNING) << "No D3dDevice found.";
+ return false;
+ }
+
+ for (size_t i = 0; i < devices.size(); i++) {
+ D3D_FEATURE_LEVEL feature_level =
+ devices[i].d3d_device()->GetFeatureLevel();
+ if (d3d_info_.max_feature_level == 0 ||
+ feature_level > d3d_info_.max_feature_level) {
+ d3d_info_.max_feature_level = feature_level;
+ }
+ if (d3d_info_.min_feature_level == 0 ||
+ feature_level < d3d_info_.min_feature_level) {
+ d3d_info_.min_feature_level = feature_level;
+ }
+
+ DxgiAdapterDuplicator duplicator(devices[i]);
+ // There may be several video cards on the system, some of them may not
+ // support IDXGOutputDuplication. But they should not impact others from
+ // taking effect, so we should continually try other adapters. This usually
+ // happens when a non-official virtual adapter is installed on the system.
+ if (!duplicator.Initialize()) {
+ RTC_LOG(LS_WARNING) << "Failed to initialize DxgiAdapterDuplicator on "
+ "adapter "
+ << i;
+ continue;
+ }
+ RTC_DCHECK(!duplicator.desktop_rect().is_empty());
+ duplicators_.push_back(std::move(duplicator));
+
+ desktop_rect_.UnionWith(duplicators_.back().desktop_rect());
+ }
+ TranslateRect();
+
+ HDC hdc = GetDC(nullptr);
+ // Use old DPI value if failed.
+ if (hdc) {
+ dpi_.set(GetDeviceCaps(hdc, LOGPIXELSX), GetDeviceCaps(hdc, LOGPIXELSY));
+ ReleaseDC(nullptr, hdc);
+ }
+
+ identity_++;
+
+ if (duplicators_.empty()) {
+ RTC_LOG(LS_WARNING)
+ << "Cannot initialize any DxgiAdapterDuplicator instance.";
+ }
+
+ return !duplicators_.empty();
+}
+
+void DxgiDuplicatorController::Deinitialize() {
+ desktop_rect_ = DesktopRect();
+ duplicators_.clear();
+ display_configuration_monitor_.Reset();
+}
+
+bool DxgiDuplicatorController::ContextExpired(
+ const Context* const context) const {
+ RTC_DCHECK(context);
+ return context->controller_id != identity_ ||
+ context->contexts.size() != duplicators_.size();
+}
+
+void DxgiDuplicatorController::Setup(Context* context) {
+ if (ContextExpired(context)) {
+ RTC_DCHECK(context);
+ context->contexts.clear();
+ context->contexts.resize(duplicators_.size());
+ for (size_t i = 0; i < duplicators_.size(); i++) {
+ duplicators_[i].Setup(&context->contexts[i]);
+ }
+ context->controller_id = identity_;
+ }
+}
+
+bool DxgiDuplicatorController::DoDuplicateUnlocked(Context* context,
+ int monitor_id,
+ SharedDesktopFrame* target) {
+ Setup(context);
+
+ if (!EnsureFrameCaptured(context, target)) {
+ return false;
+ }
+
+ bool result = false;
+ if (monitor_id < 0) {
+ // Capture entire screen.
+ result = DoDuplicateAll(context, target);
+ } else {
+ result = DoDuplicateOne(context, monitor_id, target);
+ }
+
+ if (result) {
+ target->set_dpi(dpi_);
+ return true;
+ }
+
+ return false;
+}
+
+bool DxgiDuplicatorController::DoDuplicateAll(Context* context,
+ SharedDesktopFrame* target) {
+ for (size_t i = 0; i < duplicators_.size(); i++) {
+ if (!duplicators_[i].Duplicate(&context->contexts[i], target)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool DxgiDuplicatorController::DoDuplicateOne(Context* context,
+ int monitor_id,
+ SharedDesktopFrame* target) {
+ RTC_DCHECK(monitor_id >= 0);
+ for (size_t i = 0; i < duplicators_.size() && i < context->contexts.size();
+ i++) {
+ if (monitor_id >= duplicators_[i].screen_count()) {
+ monitor_id -= duplicators_[i].screen_count();
+ } else {
+ if (duplicators_[i].DuplicateMonitor(&context->contexts[i], monitor_id,
+ target)) {
+ target->set_top_left(duplicators_[i].ScreenRect(monitor_id).top_left());
+ return true;
+ }
+ return false;
+ }
+ }
+ return false;
+}
+
+int64_t DxgiDuplicatorController::GetNumFramesCaptured() const {
+ int64_t min = INT64_MAX;
+ for (const auto& duplicator : duplicators_) {
+ min = std::min(min, duplicator.GetNumFramesCaptured());
+ }
+
+ return min;
+}
+
+DesktopSize DxgiDuplicatorController::desktop_size() const {
+ return desktop_rect_.size();
+}
+
+DesktopRect DxgiDuplicatorController::ScreenRect(int id) const {
+ RTC_DCHECK(id >= 0);
+ for (size_t i = 0; i < duplicators_.size(); i++) {
+ if (id >= duplicators_[i].screen_count()) {
+ id -= duplicators_[i].screen_count();
+ } else {
+ return duplicators_[i].ScreenRect(id);
+ }
+ }
+ return DesktopRect();
+}
+
+int DxgiDuplicatorController::ScreenCountUnlocked() const {
+ int result = 0;
+ for (auto& duplicator : duplicators_) {
+ result += duplicator.screen_count();
+ }
+ return result;
+}
+
+void DxgiDuplicatorController::GetDeviceNamesUnlocked(
+ std::vector<std::string>* output) const {
+ RTC_DCHECK(output);
+ for (auto& duplicator : duplicators_) {
+ for (int i = 0; i < duplicator.screen_count(); i++) {
+ output->push_back(duplicator.GetDeviceName(i));
+ }
+ }
+}
+
+DesktopSize DxgiDuplicatorController::SelectedDesktopSize(
+ int monitor_id) const {
+ if (monitor_id < 0) {
+ return desktop_size();
+ }
+
+ return ScreenRect(monitor_id).size();
+}
+
+bool DxgiDuplicatorController::EnsureFrameCaptured(Context* context,
+ SharedDesktopFrame* target) {
+ // On a modern system, the FPS / monitor refresh rate is usually larger than
+ // or equal to 60. So 17 milliseconds is enough to capture at least one frame.
+ const int64_t ms_per_frame = 17;
+ // Skip frames to ensure a full frame refresh has occurred and the DXGI
+ // machinery is producing frames before this function returns.
+ int64_t frames_to_skip = 1;
+ // The total time out milliseconds for this function. If we cannot get enough
+ // frames during this time interval, this function returns false, and cause
+ // the DXGI components to be reinitialized. This usually should not happen
+ // unless the system is switching display mode when this function is being
+ // called. 500 milliseconds should be enough for ~30 frames.
+ const int64_t timeout_ms = 500;
+
+ if (GetNumFramesCaptured() == 0 && !IsConsoleSession()) {
+ // When capturing a console session, waiting for a single frame is
+ // sufficient to ensure that DXGI output duplication is working. When the
+ // session is not attached to the console, it has been observed that DXGI
+ // may produce up to 4 frames (typically 1-2 though) before stopping. When
+ // this condition occurs, no errors are returned from the output duplication
+ // API, it simply appears that nothing is changing on the screen. Thus for
+ // detached sessions, we need to capture a few extra frames before we can be
+ // confident that output duplication was initialized properly.
+ frames_to_skip = 5;
+ }
+
+ if (GetNumFramesCaptured() >= frames_to_skip) {
+ return true;
+ }
+
+ std::unique_ptr<SharedDesktopFrame> fallback_frame;
+ SharedDesktopFrame* shared_frame = nullptr;
+ if (target->size().width() >= desktop_size().width() &&
+ target->size().height() >= desktop_size().height()) {
+ // `target` is large enough to cover entire screen, we do not need to use
+ // `fallback_frame`.
+ shared_frame = target;
+ } else {
+ fallback_frame = SharedDesktopFrame::Wrap(
+ std::unique_ptr<DesktopFrame>(new BasicDesktopFrame(desktop_size())));
+ shared_frame = fallback_frame.get();
+ }
+
+ const int64_t start_ms = rtc::TimeMillis();
+ while (GetNumFramesCaptured() < frames_to_skip) {
+ if (!DoDuplicateAll(context, shared_frame)) {
+ return false;
+ }
+
+ // Calling DoDuplicateAll() may change the number of frames captured.
+ if (GetNumFramesCaptured() >= frames_to_skip) {
+ break;
+ }
+
+ if (rtc::TimeMillis() - start_ms > timeout_ms) {
+ RTC_LOG(LS_ERROR) << "Failed to capture " << frames_to_skip
+ << " frames "
+ "within "
+ << timeout_ms << " milliseconds.";
+ return false;
+ }
+
+ // Sleep `ms_per_frame` before attempting to capture the next frame to
+ // ensure the video adapter has time to update the screen.
+ webrtc::SleepMs(ms_per_frame);
+ }
+ return true;
+}
+
+void DxgiDuplicatorController::TranslateRect() {
+ const DesktopVector position =
+ DesktopVector().subtract(desktop_rect_.top_left());
+ desktop_rect_.Translate(position);
+ for (auto& duplicator : duplicators_) {
+ duplicator.TranslateRect(position);
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h
new file mode 100644
index 0000000000..88c2939187
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_duplicator_controller.h
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_
+
+#include <d3dcommon.h>
+
+#include <atomic>
+#include <string>
+#include <vector>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "modules/desktop_capture/win/d3d_device.h"
+#include "modules/desktop_capture/win/display_configuration_monitor.h"
+#include "modules/desktop_capture/win/dxgi_adapter_duplicator.h"
+#include "modules/desktop_capture/win/dxgi_context.h"
+#include "modules/desktop_capture/win/dxgi_frame.h"
+#include "rtc_base/synchronization/mutex.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// A controller for all the objects we need to call Windows DirectX capture APIs
+// It's a singleton because only one IDXGIOutputDuplication instance per monitor
+// is allowed per application.
+//
+// Consumers should create a DxgiDuplicatorController::Context and keep it
+// throughout their lifetime, and pass it when calling Duplicate(). Consumers
+// can also call IsSupported() to determine whether the system supports DXGI
+// duplicator or not. If a previous IsSupported() function call returns true,
+// but a later Duplicate() returns false, this usually means the display mode is
+// changing. Consumers should retry after a while. (Typically 50 milliseconds,
+// but according to hardware performance, this time may vary.)
+// The underyling DxgiOutputDuplicators may take an additional reference on the
+// frame passed in to the Duplicate methods so that they can guarantee delivery
+// of new frames when requested; since if there have been no updates to the
+// surface, they may be unable to capture a frame.
+class RTC_EXPORT DxgiDuplicatorController {
+ public:
+ using Context = DxgiFrameContext;
+
+ // A collection of D3d information we are interested on, which may impact
+ // capturer performance or reliability.
+ struct D3dInfo {
+ // Each video adapter has its own D3D_FEATURE_LEVEL, so this structure
+ // contains the minimum and maximium D3D_FEATURE_LEVELs current system
+ // supports.
+ // Both fields can be 0, which is the default value to indicate no valid
+ // D3D_FEATURE_LEVEL has been retrieved from underlying OS APIs.
+ D3D_FEATURE_LEVEL min_feature_level;
+ D3D_FEATURE_LEVEL max_feature_level;
+
+ // TODO(zijiehe): Add more fields, such as manufacturer name, mode, driver
+ // version.
+ };
+
+ enum class Result {
+ SUCCEEDED,
+ UNSUPPORTED_SESSION,
+ FRAME_PREPARE_FAILED,
+ INITIALIZATION_FAILED,
+ DUPLICATION_FAILED,
+ INVALID_MONITOR_ID,
+ };
+
+ // Converts `result` into user-friendly string representation. The return
+ // value should not be used to identify error types.
+ static std::string ResultName(Result result);
+
+ // Returns the singleton instance of DxgiDuplicatorController.
+ static rtc::scoped_refptr<DxgiDuplicatorController> Instance();
+
+ // See ScreenCapturerWinDirectx::IsCurrentSessionSupported().
+ static bool IsCurrentSessionSupported();
+
+ // All the following public functions implicitly call Initialize() function.
+
+ // Detects whether the system supports DXGI based capturer.
+ bool IsSupported();
+
+ // Returns a copy of D3dInfo composed by last Initialize() function call. This
+ // function always copies the latest information into `info`. But once the
+ // function returns false, the information in `info` may not accurate.
+ bool RetrieveD3dInfo(D3dInfo* info);
+
+ // Captures current screen and writes into `frame`. May retain a reference to
+ // `frame`'s underlying |SharedDesktopFrame|.
+ // TODO(zijiehe): Windows cannot guarantee the frames returned by each
+ // IDXGIOutputDuplication are synchronized. But we are using a totally
+ // different threading model than the way Windows suggested, it's hard to
+ // synchronize them manually. We should find a way to do it.
+ Result Duplicate(DxgiFrame* frame);
+
+ // Captures one monitor and writes into target. `monitor_id` should >= 0. If
+ // `monitor_id` is greater than the total screen count of all the Duplicators,
+ // this function returns false. May retain a reference to `frame`'s underlying
+ // |SharedDesktopFrame|.
+ Result DuplicateMonitor(DxgiFrame* frame, int monitor_id);
+
+ // Returns dpi of current system. Returns an empty DesktopVector if system
+ // does not support DXGI based capturer.
+ DesktopVector dpi();
+
+ // Returns the count of screens on the system. These screens can be retrieved
+ // by an integer in the range of [0, ScreenCount()). If system does not
+ // support DXGI based capturer, this function returns 0.
+ int ScreenCount();
+
+ // Returns the device names of all screens on the system in utf8 encoding.
+ // These screens can be retrieved by an integer in the range of
+ // [0, output->size()). If system does not support DXGI based capturer, this
+ // function returns false.
+ bool GetDeviceNames(std::vector<std::string>* output);
+
+ private:
+ // DxgiFrameContext calls private Unregister(Context*) function in Reset().
+ friend void DxgiFrameContext::Reset();
+
+ // scoped_refptr<DxgiDuplicatorController> accesses private AddRef() and
+ // Release() functions.
+ friend class rtc::scoped_refptr<DxgiDuplicatorController>;
+
+ // A private constructor to ensure consumers to use
+ // DxgiDuplicatorController::Instance().
+ DxgiDuplicatorController();
+
+ // Not implemented: The singleton DxgiDuplicatorController instance should not
+ // be deleted.
+ ~DxgiDuplicatorController();
+
+ // RefCountedInterface implementations.
+ void AddRef();
+ void Release();
+
+ // Does the real duplication work. Setting `monitor_id` < 0 to capture entire
+ // screen. This function calls Initialize(). And if the duplication failed,
+ // this function calls Deinitialize() to ensure the Dxgi components can be
+ // reinitialized next time.
+ Result DoDuplicate(DxgiFrame* frame, int monitor_id);
+
+ // Unload all the DXGI components and releases the resources. This function
+ // wraps Deinitialize() with `mutex_`.
+ void Unload();
+
+ // Unregisters Context from this instance and all DxgiAdapterDuplicator(s)
+ // it owns.
+ void Unregister(const Context* const context);
+
+ // All functions below should be called in `mutex_` locked scope and should be
+ // after a successful Initialize().
+
+ // If current instance has not been initialized, executes DoInitialize()
+ // function, and returns initialize result. Otherwise directly returns true.
+ // This function may calls Deinitialize() if initialization failed.
+ bool Initialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Does the real initialization work, this function should only be called in
+ // Initialize().
+ bool DoInitialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Clears all COM components referred by this instance. So next Duplicate()
+ // call will eventually initialize this instance again.
+ void Deinitialize() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // A helper function to check whether a Context has been expired.
+ bool ContextExpired(const Context* const context) const
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Updates Context if needed.
+ void Setup(Context* context) RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ bool DoDuplicateUnlocked(Context* context,
+ int monitor_id,
+ SharedDesktopFrame* target)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Captures all monitors.
+ bool DoDuplicateAll(Context* context, SharedDesktopFrame* target)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Captures one monitor.
+ bool DoDuplicateOne(Context* context,
+ int monitor_id,
+ SharedDesktopFrame* target)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // The minimum GetNumFramesCaptured() returned by `duplicators_`.
+ int64_t GetNumFramesCaptured() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Returns a DesktopSize to cover entire `desktop_rect_`.
+ DesktopSize desktop_size() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Returns the size of one screen. `id` should be >= 0. If system does not
+ // support DXGI based capturer, or `id` is greater than the total screen count
+ // of all the Duplicators, this function returns an empty DesktopRect.
+ DesktopRect ScreenRect(int id) const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ int ScreenCountUnlocked() const RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ void GetDeviceNamesUnlocked(std::vector<std::string>* output) const
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Returns the desktop size of the selected screen `monitor_id`. Setting
+ // `monitor_id` < 0 to return the entire screen size.
+ DesktopSize SelectedDesktopSize(int monitor_id) const
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Retries DoDuplicateAll() for several times until GetNumFramesCaptured() is
+ // large enough. Returns false if DoDuplicateAll() returns false, or
+ // GetNumFramesCaptured() has never reached the requirement.
+ // According to http://crbug.com/682112, dxgi capturer returns a black frame
+ // during first several capture attempts.
+ bool EnsureFrameCaptured(Context* context, SharedDesktopFrame* target)
+ RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // Moves `desktop_rect_` and all underlying `duplicators_`, putting top left
+ // corner of the desktop at (0, 0). This is necessary because DXGI_OUTPUT_DESC
+ // may return negative coordinates. Called from DoInitialize() after all
+ // DxgiAdapterDuplicator and DxgiOutputDuplicator instances are initialized.
+ void TranslateRect() RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
+
+ // The count of references which are now "living".
+ std::atomic_int refcount_;
+
+ // This lock must be locked whenever accessing any of the following objects.
+ Mutex mutex_;
+
+ // A self-incremented integer to compare with the one in Context. It ensures
+ // a Context instance is always initialized after DxgiDuplicatorController.
+ int identity_ RTC_GUARDED_BY(mutex_) = 0;
+ DesktopRect desktop_rect_ RTC_GUARDED_BY(mutex_);
+ DesktopVector dpi_ RTC_GUARDED_BY(mutex_);
+ std::vector<DxgiAdapterDuplicator> duplicators_ RTC_GUARDED_BY(mutex_);
+ D3dInfo d3d_info_ RTC_GUARDED_BY(mutex_);
+ DisplayConfigurationMonitor display_configuration_monitor_
+ RTC_GUARDED_BY(mutex_);
+ // A number to indicate how many succeeded duplications have been performed.
+ uint32_t succeeded_duplications_ RTC_GUARDED_BY(mutex_) = 0;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_DUPLICATOR_CONTROLLER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.cc
new file mode 100644
index 0000000000..13d5b4b62e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.cc
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/dxgi_frame.h"
+
+#include <string.h>
+
+#include <utility>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/win/dxgi_duplicator_controller.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+DxgiFrame::DxgiFrame(SharedMemoryFactory* factory) : factory_(factory) {}
+
+DxgiFrame::~DxgiFrame() = default;
+
+bool DxgiFrame::Prepare(DesktopSize size, DesktopCapturer::SourceId source_id) {
+ if (source_id != source_id_) {
+ // Once the source has been changed, the entire source should be copied.
+ source_id_ = source_id;
+ context_.Reset();
+ }
+
+ if (resolution_tracker_.SetResolution(size)) {
+ // Once the output size changed, recreate the SharedDesktopFrame.
+ frame_.reset();
+ }
+
+ if (!frame_) {
+ std::unique_ptr<DesktopFrame> frame;
+ if (factory_) {
+ frame = SharedMemoryDesktopFrame::Create(size, factory_);
+
+ if (!frame) {
+ RTC_LOG(LS_WARNING) << "DxgiFrame cannot create a new DesktopFrame.";
+ return false;
+ }
+
+ // DirectX capturer won't paint each pixel in the frame due to its one
+ // capturer per monitor design. So once the new frame is created, we
+ // should clear it to avoid the legacy image to be remained on it. See
+ // http://crbug.com/708766.
+ RTC_DCHECK_EQ(frame->stride(),
+ frame->size().width() * DesktopFrame::kBytesPerPixel);
+ memset(frame->data(), 0, frame->stride() * frame->size().height());
+ } else {
+ frame.reset(new BasicDesktopFrame(size));
+ }
+
+ frame_ = SharedDesktopFrame::Wrap(std::move(frame));
+ }
+
+ return !!frame_;
+}
+
+SharedDesktopFrame* DxgiFrame::frame() const {
+ RTC_DCHECK(frame_);
+ return frame_.get();
+}
+
+DxgiFrame::Context* DxgiFrame::context() {
+ RTC_DCHECK(frame_);
+ return &context_;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.h
new file mode 100644
index 0000000000..6a9ce868a7
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_frame.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_DXGI_FRAME_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_FRAME_H_
+
+#include <memory>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/resolution_tracker.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "modules/desktop_capture/shared_memory.h"
+#include "modules/desktop_capture/win/dxgi_context.h"
+
+namespace webrtc {
+
+class DxgiDuplicatorController;
+
+// A pair of a SharedDesktopFrame and a DxgiDuplicatorController::Context for
+// the client of DxgiDuplicatorController.
+class DxgiFrame final {
+ public:
+ using Context = DxgiFrameContext;
+
+ // DxgiFrame does not take ownership of `factory`, consumers should ensure it
+ // outlives this instance. nullptr is acceptable.
+ explicit DxgiFrame(SharedMemoryFactory* factory);
+ ~DxgiFrame();
+
+ // Should not be called if Prepare() is not executed or returns false.
+ SharedDesktopFrame* frame() const;
+
+ private:
+ // Allows DxgiDuplicatorController to access Prepare() and context() function
+ // as well as Context class.
+ friend class DxgiDuplicatorController;
+
+ // Prepares current instance with desktop size and source id.
+ bool Prepare(DesktopSize size, DesktopCapturer::SourceId source_id);
+
+ // Should not be called if Prepare() is not executed or returns false.
+ Context* context();
+
+ SharedMemoryFactory* const factory_;
+ ResolutionTracker resolution_tracker_;
+ DesktopCapturer::SourceId source_id_ = kFullDesktopScreenId;
+ std::unique_ptr<SharedDesktopFrame> frame_;
+ Context context_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_FRAME_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc
new file mode 100644
index 0000000000..caad6f43d1
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.cc
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/dxgi_output_duplicator.h"
+
+#include <dxgi.h>
+#include <dxgiformat.h>
+#include <string.h>
+#include <unknwn.h>
+#include <windows.h>
+
+#include <algorithm>
+
+#include "modules/desktop_capture/win/desktop_capture_utils.h"
+#include "modules/desktop_capture/win/dxgi_texture_mapping.h"
+#include "modules/desktop_capture/win/dxgi_texture_staging.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/string_utils.h"
+#include "rtc_base/win32.h"
+
+namespace webrtc {
+
+using Microsoft::WRL::ComPtr;
+
+namespace {
+
+// Timeout for AcquireNextFrame() call.
+// DxgiDuplicatorController leverages external components to do the capture
+// scheduling. So here DxgiOutputDuplicator does not need to actively wait for a
+// new frame.
+const int kAcquireTimeoutMs = 0;
+
+DesktopRect RECTToDesktopRect(const RECT& rect) {
+ return DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+}
+
+Rotation DxgiRotationToRotation(DXGI_MODE_ROTATION rotation) {
+ switch (rotation) {
+ case DXGI_MODE_ROTATION_IDENTITY:
+ case DXGI_MODE_ROTATION_UNSPECIFIED:
+ return Rotation::CLOCK_WISE_0;
+ case DXGI_MODE_ROTATION_ROTATE90:
+ return Rotation::CLOCK_WISE_90;
+ case DXGI_MODE_ROTATION_ROTATE180:
+ return Rotation::CLOCK_WISE_180;
+ case DXGI_MODE_ROTATION_ROTATE270:
+ return Rotation::CLOCK_WISE_270;
+ }
+
+ RTC_DCHECK_NOTREACHED();
+ return Rotation::CLOCK_WISE_0;
+}
+
+} // namespace
+
+DxgiOutputDuplicator::DxgiOutputDuplicator(const D3dDevice& device,
+ const ComPtr<IDXGIOutput1>& output,
+ const DXGI_OUTPUT_DESC& desc)
+ : device_(device),
+ output_(output),
+ device_name_(rtc::ToUtf8(desc.DeviceName)),
+ desktop_rect_(RECTToDesktopRect(desc.DesktopCoordinates)) {
+ RTC_DCHECK(output_);
+ RTC_DCHECK(!desktop_rect_.is_empty());
+ RTC_DCHECK_GT(desktop_rect_.width(), 0);
+ RTC_DCHECK_GT(desktop_rect_.height(), 0);
+}
+
+DxgiOutputDuplicator::DxgiOutputDuplicator(DxgiOutputDuplicator&& other) =
+ default;
+
+DxgiOutputDuplicator::~DxgiOutputDuplicator() {
+ if (duplication_) {
+ duplication_->ReleaseFrame();
+ }
+ texture_.reset();
+}
+
+bool DxgiOutputDuplicator::Initialize() {
+ if (DuplicateOutput()) {
+ if (desc_.DesktopImageInSystemMemory) {
+ texture_.reset(new DxgiTextureMapping(duplication_.Get()));
+ } else {
+ texture_.reset(new DxgiTextureStaging(device_));
+ }
+ return true;
+ } else {
+ duplication_.Reset();
+ return false;
+ }
+}
+
+bool DxgiOutputDuplicator::DuplicateOutput() {
+ RTC_DCHECK(!duplication_);
+ _com_error error =
+ output_->DuplicateOutput(static_cast<IUnknown*>(device_.d3d_device()),
+ duplication_.GetAddressOf());
+ if (error.Error() != S_OK || !duplication_) {
+ RTC_LOG(LS_WARNING) << "Failed to duplicate output from IDXGIOutput1: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ memset(&desc_, 0, sizeof(desc_));
+ duplication_->GetDesc(&desc_);
+ if (desc_.ModeDesc.Format != DXGI_FORMAT_B8G8R8A8_UNORM) {
+ RTC_LOG(LS_ERROR) << "IDXGIDuplicateOutput does not use RGBA (8 bit) "
+ << "format, which is required by downstream components, "
+ << "format is " << desc_.ModeDesc.Format;
+ return false;
+ }
+
+ if (static_cast<int>(desc_.ModeDesc.Width) != desktop_rect_.width() ||
+ static_cast<int>(desc_.ModeDesc.Height) != desktop_rect_.height()) {
+ RTC_LOG(LS_ERROR)
+ << "IDXGIDuplicateOutput does not return a same size as its "
+ << "IDXGIOutput1, size returned by IDXGIDuplicateOutput is "
+ << desc_.ModeDesc.Width << " x " << desc_.ModeDesc.Height
+ << ", size returned by IDXGIOutput1 is " << desktop_rect_.width()
+ << " x " << desktop_rect_.height();
+ return false;
+ }
+
+ rotation_ = DxgiRotationToRotation(desc_.Rotation);
+ unrotated_size_ = RotateSize(desktop_size(), ReverseRotation(rotation_));
+
+ return true;
+}
+
+bool DxgiOutputDuplicator::ReleaseFrame() {
+ RTC_DCHECK(duplication_);
+ _com_error error = duplication_->ReleaseFrame();
+ if (error.Error() != S_OK) {
+ RTC_LOG(LS_ERROR) << "Failed to release frame from IDXGIOutputDuplication: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+ return true;
+}
+
+bool DxgiOutputDuplicator::Duplicate(Context* context,
+ DesktopVector offset,
+ SharedDesktopFrame* target) {
+ RTC_DCHECK(duplication_);
+ RTC_DCHECK(texture_);
+ RTC_DCHECK(target);
+ if (!DesktopRect::MakeSize(target->size())
+ .ContainsRect(GetTranslatedDesktopRect(offset))) {
+ // target size is not large enough to cover current output region.
+ return false;
+ }
+
+ DXGI_OUTDUPL_FRAME_INFO frame_info;
+ memset(&frame_info, 0, sizeof(frame_info));
+ ComPtr<IDXGIResource> resource;
+ _com_error error = duplication_->AcquireNextFrame(
+ kAcquireTimeoutMs, &frame_info, resource.GetAddressOf());
+ if (error.Error() != S_OK && error.Error() != DXGI_ERROR_WAIT_TIMEOUT) {
+ RTC_LOG(LS_ERROR) << "Failed to capture frame: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ // We need to merge updated region with the one from context, but only spread
+ // updated region from current frame. So keeps a copy of updated region from
+ // context here. The `updated_region` always starts from (0, 0).
+ DesktopRegion updated_region;
+ updated_region.Swap(&context->updated_region);
+ if (error.Error() == S_OK && frame_info.AccumulatedFrames > 0 && resource) {
+ DetectUpdatedRegion(frame_info, &context->updated_region);
+ SpreadContextChange(context);
+ if (!texture_->CopyFrom(frame_info, resource.Get())) {
+ return false;
+ }
+ updated_region.AddRegion(context->updated_region);
+ // TODO(zijiehe): Figure out why clearing context->updated_region() here
+ // triggers screen flickering?
+
+ const DesktopFrame& source = texture_->AsDesktopFrame();
+ if (rotation_ != Rotation::CLOCK_WISE_0) {
+ for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
+ it.Advance()) {
+ // The `updated_region` returned by Windows is rotated, but the `source`
+ // frame is not. So we need to rotate it reversely.
+ const DesktopRect source_rect =
+ RotateRect(it.rect(), desktop_size(), ReverseRotation(rotation_));
+ RotateDesktopFrame(source, source_rect, rotation_, offset, target);
+ }
+ } else {
+ for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
+ it.Advance()) {
+ // The DesktopRect in `target`, starts from offset.
+ DesktopRect dest_rect = it.rect();
+ dest_rect.Translate(offset);
+ target->CopyPixelsFrom(source, it.rect().top_left(), dest_rect);
+ }
+ }
+ last_frame_ = target->Share();
+ last_frame_offset_ = offset;
+ updated_region.Translate(offset.x(), offset.y());
+ target->mutable_updated_region()->AddRegion(updated_region);
+ num_frames_captured_++;
+ return texture_->Release() && ReleaseFrame();
+ }
+
+ if (last_frame_) {
+ // No change since last frame or AcquireNextFrame() timed out, we will
+ // export last frame to the target.
+ for (DesktopRegion::Iterator it(updated_region); !it.IsAtEnd();
+ it.Advance()) {
+ // The DesktopRect in `source`, starts from last_frame_offset_.
+ DesktopRect source_rect = it.rect();
+ // The DesktopRect in `target`, starts from offset.
+ DesktopRect target_rect = source_rect;
+ source_rect.Translate(last_frame_offset_);
+ target_rect.Translate(offset);
+ target->CopyPixelsFrom(*last_frame_, source_rect.top_left(), target_rect);
+ }
+ updated_region.Translate(offset.x(), offset.y());
+ target->mutable_updated_region()->AddRegion(updated_region);
+ } else {
+ // If we were at the very first frame, and capturing failed, the
+ // context->updated_region should be kept unchanged for next attempt.
+ context->updated_region.Swap(&updated_region);
+ }
+ // If AcquireNextFrame() failed with timeout error, we do not need to release
+ // the frame.
+ return error.Error() == DXGI_ERROR_WAIT_TIMEOUT || ReleaseFrame();
+}
+
+DesktopRect DxgiOutputDuplicator::GetTranslatedDesktopRect(
+ DesktopVector offset) const {
+ DesktopRect result(DesktopRect::MakeSize(desktop_size()));
+ result.Translate(offset);
+ return result;
+}
+
+DesktopRect DxgiOutputDuplicator::GetUntranslatedDesktopRect() const {
+ return DesktopRect::MakeSize(desktop_size());
+}
+
+void DxgiOutputDuplicator::DetectUpdatedRegion(
+ const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ DesktopRegion* updated_region) {
+ if (DoDetectUpdatedRegion(frame_info, updated_region)) {
+ // Make sure even a region returned by Windows API is out of the scope of
+ // desktop_rect_, we still won't export it to the target DesktopFrame.
+ updated_region->IntersectWith(GetUntranslatedDesktopRect());
+ } else {
+ updated_region->SetRect(GetUntranslatedDesktopRect());
+ }
+}
+
+bool DxgiOutputDuplicator::DoDetectUpdatedRegion(
+ const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ DesktopRegion* updated_region) {
+ RTC_DCHECK(updated_region);
+ updated_region->Clear();
+ if (frame_info.TotalMetadataBufferSize == 0) {
+ // This should not happen, since frame_info.AccumulatedFrames > 0.
+ RTC_LOG(LS_ERROR) << "frame_info.AccumulatedFrames > 0, "
+ << "but TotalMetadataBufferSize == 0";
+ return false;
+ }
+
+ if (metadata_.size() < frame_info.TotalMetadataBufferSize) {
+ metadata_.clear(); // Avoid data copy
+ metadata_.resize(frame_info.TotalMetadataBufferSize);
+ }
+
+ UINT buff_size = 0;
+ DXGI_OUTDUPL_MOVE_RECT* move_rects =
+ reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(metadata_.data());
+ size_t move_rects_count = 0;
+ _com_error error = duplication_->GetFrameMoveRects(
+ static_cast<UINT>(metadata_.size()), move_rects, &buff_size);
+ if (error.Error() != S_OK) {
+ RTC_LOG(LS_ERROR) << "Failed to get move rectangles: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+ move_rects_count = buff_size / sizeof(DXGI_OUTDUPL_MOVE_RECT);
+
+ RECT* dirty_rects = reinterpret_cast<RECT*>(metadata_.data() + buff_size);
+ size_t dirty_rects_count = 0;
+ error = duplication_->GetFrameDirtyRects(
+ static_cast<UINT>(metadata_.size()) - buff_size, dirty_rects, &buff_size);
+ if (error.Error() != S_OK) {
+ RTC_LOG(LS_ERROR) << "Failed to get dirty rectangles: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+ dirty_rects_count = buff_size / sizeof(RECT);
+
+ while (move_rects_count > 0) {
+ // DirectX capturer API may randomly return unmoved move_rects, which should
+ // be skipped to avoid unnecessary wasting of differing and encoding
+ // resources.
+ // By using testing application it2me_standalone_host_main, this check
+ // reduces average capture time by 0.375% (4.07 -> 4.055), and average
+ // encode time by 0.313% (8.042 -> 8.016) without other impacts.
+ if (move_rects->SourcePoint.x != move_rects->DestinationRect.left ||
+ move_rects->SourcePoint.y != move_rects->DestinationRect.top) {
+ updated_region->AddRect(
+ RotateRect(DesktopRect::MakeXYWH(move_rects->SourcePoint.x,
+ move_rects->SourcePoint.y,
+ move_rects->DestinationRect.right -
+ move_rects->DestinationRect.left,
+ move_rects->DestinationRect.bottom -
+ move_rects->DestinationRect.top),
+ unrotated_size_, rotation_));
+ updated_region->AddRect(
+ RotateRect(DesktopRect::MakeLTRB(move_rects->DestinationRect.left,
+ move_rects->DestinationRect.top,
+ move_rects->DestinationRect.right,
+ move_rects->DestinationRect.bottom),
+ unrotated_size_, rotation_));
+ } else {
+ RTC_LOG(LS_INFO) << "Unmoved move_rect detected, ["
+ << move_rects->DestinationRect.left << ", "
+ << move_rects->DestinationRect.top << "] - ["
+ << move_rects->DestinationRect.right << ", "
+ << move_rects->DestinationRect.bottom << "].";
+ }
+ move_rects++;
+ move_rects_count--;
+ }
+
+ while (dirty_rects_count > 0) {
+ updated_region->AddRect(RotateRect(
+ DesktopRect::MakeLTRB(dirty_rects->left, dirty_rects->top,
+ dirty_rects->right, dirty_rects->bottom),
+ unrotated_size_, rotation_));
+ dirty_rects++;
+ dirty_rects_count--;
+ }
+
+ return true;
+}
+
+void DxgiOutputDuplicator::Setup(Context* context) {
+ RTC_DCHECK(context->updated_region.is_empty());
+ // Always copy entire monitor during the first Duplicate() function call.
+ context->updated_region.AddRect(GetUntranslatedDesktopRect());
+ RTC_DCHECK(std::find(contexts_.begin(), contexts_.end(), context) ==
+ contexts_.end());
+ contexts_.push_back(context);
+}
+
+void DxgiOutputDuplicator::Unregister(const Context* const context) {
+ auto it = std::find(contexts_.begin(), contexts_.end(), context);
+ RTC_DCHECK(it != contexts_.end());
+ contexts_.erase(it);
+}
+
+void DxgiOutputDuplicator::SpreadContextChange(const Context* const source) {
+ for (Context* dest : contexts_) {
+ RTC_DCHECK(dest);
+ if (dest != source) {
+ dest->updated_region.AddRegion(source->updated_region);
+ }
+ }
+}
+
+DesktopSize DxgiOutputDuplicator::desktop_size() const {
+ return desktop_rect_.size();
+}
+
+int64_t DxgiOutputDuplicator::num_frames_captured() const {
+#if !defined(NDEBUG)
+ RTC_DCHECK_EQ(!!last_frame_, num_frames_captured_ > 0);
+#endif
+ return num_frames_captured_;
+}
+
+void DxgiOutputDuplicator::TranslateRect(const DesktopVector& position) {
+ desktop_rect_.Translate(position);
+ RTC_DCHECK_GE(desktop_rect_.left(), 0);
+ RTC_DCHECK_GE(desktop_rect_.top(), 0);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.h
new file mode 100644
index 0000000000..df15fe566e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_output_duplicator.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_DXGI_OUTPUT_DUPLICATOR_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_OUTPUT_DUPLICATOR_H_
+
+#include <comdef.h>
+#include <dxgi.h>
+#include <dxgi1_2.h>
+#include <wrl/client.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_frame_rotation.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "modules/desktop_capture/win/d3d_device.h"
+#include "modules/desktop_capture/win/dxgi_context.h"
+#include "modules/desktop_capture/win/dxgi_texture.h"
+#include "rtc_base/thread_annotations.h"
+
+namespace webrtc {
+
+// Duplicates the content on one IDXGIOutput, i.e. one monitor attached to one
+// video card. None of functions in this class is thread-safe.
+class DxgiOutputDuplicator {
+ public:
+ using Context = DxgiOutputContext;
+
+ // Creates an instance of DxgiOutputDuplicator from a D3dDevice and one of its
+ // IDXGIOutput1. Caller must maintain the lifetime of device, to make sure it
+ // outlives this instance. Only DxgiAdapterDuplicator can create an instance.
+ DxgiOutputDuplicator(const D3dDevice& device,
+ const Microsoft::WRL::ComPtr<IDXGIOutput1>& output,
+ const DXGI_OUTPUT_DESC& desc);
+
+ // To allow this class to work with vector.
+ DxgiOutputDuplicator(DxgiOutputDuplicator&& other);
+
+ // Destructs this instance. We need to make sure texture_ has been released
+ // before duplication_.
+ ~DxgiOutputDuplicator();
+
+ // Initializes duplication_ object.
+ bool Initialize();
+
+ // Copies the content of current IDXGIOutput to the `target`. To improve the
+ // performance, this function copies only regions merged from
+ // `context`->updated_region and DetectUpdatedRegion(). The `offset` decides
+ // the offset in the `target` where the content should be copied to. i.e. this
+ // function copies the content to the rectangle of (offset.x(), offset.y()) to
+ // (offset.x() + desktop_rect_.width(), offset.y() + desktop_rect_.height()).
+ // Returns false in case of a failure.
+ // May retain a reference to `target` so that a "captured" frame can be
+ // returned in the event that a new frame is not ready to be captured yet.
+ // (Or in other words, if the call to IDXGIOutputDuplication::AcquireNextFrame
+ // indicates that there is not yet a new frame, this is usually because no
+ // updates have occurred to the frame).
+ bool Duplicate(Context* context,
+ DesktopVector offset,
+ SharedDesktopFrame* target);
+
+ // Returns the desktop rect covered by this DxgiOutputDuplicator.
+ DesktopRect desktop_rect() const { return desktop_rect_; }
+
+ // Returns the device name from DXGI_OUTPUT_DESC in utf8 encoding.
+ const std::string& device_name() const { return device_name_; }
+
+ void Setup(Context* context);
+
+ void Unregister(const Context* const context);
+
+ // How many frames have been captured by this DxigOutputDuplicator.
+ int64_t num_frames_captured() const;
+
+ // Moves `desktop_rect_`. See DxgiDuplicatorController::TranslateRect().
+ void TranslateRect(const DesktopVector& position);
+
+ private:
+ // Calls DoDetectUpdatedRegion(). If it fails, this function sets the
+ // `updated_region` as entire UntranslatedDesktopRect().
+ void DetectUpdatedRegion(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ DesktopRegion* updated_region);
+
+ // Returns untranslated updated region, which are directly returned by Windows
+ // APIs. Returns false in case of a failure.
+ bool DoDetectUpdatedRegion(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ DesktopRegion* updated_region);
+
+ bool ReleaseFrame();
+
+ // Initializes duplication_ instance. Expects duplication_ is in empty status.
+ // Returns false if system does not support IDXGIOutputDuplication.
+ bool DuplicateOutput();
+
+ // Returns a DesktopRect with the same size of desktop_size(), but translated
+ // by offset.
+ DesktopRect GetTranslatedDesktopRect(DesktopVector offset) const;
+
+ // Returns a DesktopRect with the same size of desktop_size(), but starts from
+ // (0, 0).
+ DesktopRect GetUntranslatedDesktopRect() const;
+
+ // Spreads changes from `context` to other registered Context(s) in
+ // contexts_.
+ void SpreadContextChange(const Context* const context);
+
+ // Returns the size of desktop rectangle current instance representing.
+ DesktopSize desktop_size() const;
+
+ const D3dDevice device_;
+ const Microsoft::WRL::ComPtr<IDXGIOutput1> output_;
+ const std::string device_name_;
+ DesktopRect desktop_rect_;
+ Microsoft::WRL::ComPtr<IDXGIOutputDuplication> duplication_;
+ DXGI_OUTDUPL_DESC desc_;
+ std::vector<uint8_t> metadata_;
+ std::unique_ptr<DxgiTexture> texture_;
+ Rotation rotation_;
+ DesktopSize unrotated_size_;
+
+ // After each AcquireNextFrame() function call, updated_region_(s) of all
+ // active Context(s) need to be updated. Since they have missed the
+ // change this time. And during next Duplicate() function call, their
+ // updated_region_ will be merged and copied.
+ std::vector<Context*> contexts_;
+
+ // The last full frame of this output and its offset. If on AcquireNextFrame()
+ // failed because of timeout, i.e. no update, we can copy content from
+ // `last_frame_`.
+ std::unique_ptr<SharedDesktopFrame> last_frame_;
+ DesktopVector last_frame_offset_;
+
+ int64_t num_frames_captured_ = 0;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_OUTPUT_DUPLICATOR_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.cc
new file mode 100644
index 0000000000..b8f5b81f90
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/dxgi_texture.h"
+
+#include <comdef.h>
+#include <d3d11.h>
+#include <wrl/client.h>
+
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/win/desktop_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+using Microsoft::WRL::ComPtr;
+
+namespace webrtc {
+
+namespace {
+
+class DxgiDesktopFrame : public DesktopFrame {
+ public:
+ explicit DxgiDesktopFrame(const DxgiTexture& texture)
+ : DesktopFrame(texture.desktop_size(),
+ texture.pitch(),
+ texture.bits(),
+ nullptr) {}
+
+ ~DxgiDesktopFrame() override = default;
+};
+
+} // namespace
+
+DxgiTexture::DxgiTexture() = default;
+DxgiTexture::~DxgiTexture() = default;
+
+bool DxgiTexture::CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ IDXGIResource* resource) {
+ RTC_DCHECK_GT(frame_info.AccumulatedFrames, 0);
+ RTC_DCHECK(resource);
+ ComPtr<ID3D11Texture2D> texture;
+ _com_error error = resource->QueryInterface(
+ __uuidof(ID3D11Texture2D),
+ reinterpret_cast<void**>(texture.GetAddressOf()));
+ if (error.Error() != S_OK || !texture) {
+ RTC_LOG(LS_ERROR) << "Failed to convert IDXGIResource to ID3D11Texture2D: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ D3D11_TEXTURE2D_DESC desc = {0};
+ texture->GetDesc(&desc);
+ desktop_size_.set(desc.Width, desc.Height);
+
+ return CopyFromTexture(frame_info, texture.Get());
+}
+
+const DesktopFrame& DxgiTexture::AsDesktopFrame() {
+ if (!frame_) {
+ frame_.reset(new DxgiDesktopFrame(*this));
+ }
+ return *frame_;
+}
+
+bool DxgiTexture::Release() {
+ frame_.reset();
+ return DoRelease();
+}
+
+DXGI_MAPPED_RECT* DxgiTexture::rect() {
+ return &rect_;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.h
new file mode 100644
index 0000000000..a663b95a04
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_H_
+
+#include <d3d11.h>
+#include <dxgi1_2.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+class DesktopRegion;
+
+// A texture copied or mapped from a DXGI_OUTDUPL_FRAME_INFO and IDXGIResource.
+class DxgiTexture {
+ public:
+ // Creates a DxgiTexture instance, which represents the `desktop_size` area of
+ // entire screen -- usually a monitor on the system.
+ DxgiTexture();
+
+ virtual ~DxgiTexture();
+
+ // Copies selected regions of a frame represented by frame_info and resource.
+ // Returns false if anything wrong.
+ bool CopyFrom(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ IDXGIResource* resource);
+
+ const DesktopSize& desktop_size() const { return desktop_size_; }
+
+ uint8_t* bits() const { return static_cast<uint8_t*>(rect_.pBits); }
+
+ int pitch() const { return static_cast<int>(rect_.Pitch); }
+
+ // Releases the resource currently holds by this instance. Returns false if
+ // anything wrong, and this instance should be deprecated in this state. bits,
+ // pitch and AsDesktopFrame are only valid after a success CopyFrom() call,
+ // but before Release() call.
+ bool Release();
+
+ // Returns a DesktopFrame snapshot of a DxgiTexture instance. This
+ // DesktopFrame is used to copy a DxgiTexture content to another DesktopFrame
+ // only. And it should not outlive its DxgiTexture instance.
+ const DesktopFrame& AsDesktopFrame();
+
+ protected:
+ DXGI_MAPPED_RECT* rect();
+
+ virtual bool CopyFromTexture(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ ID3D11Texture2D* texture) = 0;
+
+ virtual bool DoRelease() = 0;
+
+ private:
+ DXGI_MAPPED_RECT rect_ = {0};
+ DesktopSize desktop_size_;
+ std::unique_ptr<DesktopFrame> frame_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc
new file mode 100644
index 0000000000..7ecf1adc61
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/dxgi_texture_mapping.h"
+
+#include <comdef.h>
+#include <dxgi.h>
+#include <dxgi1_2.h>
+
+#include "modules/desktop_capture/win/desktop_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+
+DxgiTextureMapping::DxgiTextureMapping(IDXGIOutputDuplication* duplication)
+ : duplication_(duplication) {
+ RTC_DCHECK(duplication_);
+}
+
+DxgiTextureMapping::~DxgiTextureMapping() = default;
+
+bool DxgiTextureMapping::CopyFromTexture(
+ const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ ID3D11Texture2D* texture) {
+ RTC_DCHECK_GT(frame_info.AccumulatedFrames, 0);
+ RTC_DCHECK(texture);
+ *rect() = {0};
+ _com_error error = duplication_->MapDesktopSurface(rect());
+ if (error.Error() != S_OK) {
+ *rect() = {0};
+ RTC_LOG(LS_ERROR)
+ << "Failed to map the IDXGIOutputDuplication to a bitmap: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ return true;
+}
+
+bool DxgiTextureMapping::DoRelease() {
+ _com_error error = duplication_->UnMapDesktopSurface();
+ if (error.Error() != S_OK) {
+ RTC_LOG(LS_ERROR) << "Failed to unmap the IDXGIOutputDuplication: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.h
new file mode 100644
index 0000000000..71f00b99ab
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_mapping.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_MAPPING_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_MAPPING_H_
+
+#include <d3d11.h>
+#include <dxgi1_2.h>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/win/dxgi_texture.h"
+
+namespace webrtc {
+
+// A DxgiTexture which directly maps bitmap from IDXGIResource. This class is
+// used when DXGI_OUTDUPL_DESC.DesktopImageInSystemMemory is true. (This usually
+// means the video card shares main memory with CPU, instead of having its own
+// individual memory.)
+class DxgiTextureMapping : public DxgiTexture {
+ public:
+ // Creates a DxgiTextureMapping instance. Caller must maintain the lifetime
+ // of input `duplication` to make sure it outlives this instance.
+ explicit DxgiTextureMapping(IDXGIOutputDuplication* duplication);
+
+ ~DxgiTextureMapping() override;
+
+ protected:
+ bool CopyFromTexture(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ ID3D11Texture2D* texture) override;
+
+ bool DoRelease() override;
+
+ private:
+ IDXGIOutputDuplication* const duplication_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_MAPPING_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.cc b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.cc
new file mode 100644
index 0000000000..17e8518a7d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.cc
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/dxgi_texture_staging.h"
+
+#include <comdef.h>
+#include <dxgi.h>
+#include <dxgi1_2.h>
+#include <unknwn.h>
+
+#include "modules/desktop_capture/win/desktop_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "system_wrappers/include/metrics.h"
+
+using Microsoft::WRL::ComPtr;
+
+namespace webrtc {
+
+DxgiTextureStaging::DxgiTextureStaging(const D3dDevice& device)
+ : device_(device) {}
+
+DxgiTextureStaging::~DxgiTextureStaging() = default;
+
+bool DxgiTextureStaging::InitializeStage(ID3D11Texture2D* texture) {
+ RTC_DCHECK(texture);
+ D3D11_TEXTURE2D_DESC desc = {0};
+ texture->GetDesc(&desc);
+
+ desc.ArraySize = 1;
+ desc.BindFlags = 0;
+ desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ desc.MipLevels = 1;
+ desc.MiscFlags = 0;
+ desc.SampleDesc.Count = 1;
+ desc.SampleDesc.Quality = 0;
+ desc.Usage = D3D11_USAGE_STAGING;
+ if (stage_) {
+ AssertStageAndSurfaceAreSameObject();
+ D3D11_TEXTURE2D_DESC current_desc;
+ stage_->GetDesc(&current_desc);
+ const bool recreate_needed =
+ (memcmp(&desc, &current_desc, sizeof(D3D11_TEXTURE2D_DESC)) != 0);
+ RTC_HISTOGRAM_BOOLEAN("WebRTC.DesktopCapture.StagingTextureRecreate",
+ recreate_needed);
+ if (!recreate_needed) {
+ return true;
+ }
+
+ // The descriptions are not consistent, we need to create a new
+ // ID3D11Texture2D instance.
+ stage_.Reset();
+ surface_.Reset();
+ } else {
+ RTC_DCHECK(!surface_);
+ }
+
+ _com_error error = device_.d3d_device()->CreateTexture2D(
+ &desc, nullptr, stage_.GetAddressOf());
+ if (error.Error() != S_OK || !stage_) {
+ RTC_LOG(LS_ERROR) << "Failed to create a new ID3D11Texture2D as stage: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ error = stage_.As(&surface_);
+ if (error.Error() != S_OK || !surface_) {
+ RTC_LOG(LS_ERROR) << "Failed to convert ID3D11Texture2D to IDXGISurface: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ return true;
+}
+
+void DxgiTextureStaging::AssertStageAndSurfaceAreSameObject() {
+ ComPtr<IUnknown> left;
+ ComPtr<IUnknown> right;
+ bool left_result = SUCCEEDED(stage_.As(&left));
+ bool right_result = SUCCEEDED(surface_.As(&right));
+ RTC_DCHECK(left_result);
+ RTC_DCHECK(right_result);
+ RTC_DCHECK(left.Get() == right.Get());
+}
+
+bool DxgiTextureStaging::CopyFromTexture(
+ const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ ID3D11Texture2D* texture) {
+ RTC_DCHECK_GT(frame_info.AccumulatedFrames, 0);
+ RTC_DCHECK(texture);
+
+ // AcquireNextFrame returns a CPU inaccessible IDXGIResource, so we need to
+ // copy it to a CPU accessible staging ID3D11Texture2D.
+ if (!InitializeStage(texture)) {
+ return false;
+ }
+
+ device_.context()->CopyResource(static_cast<ID3D11Resource*>(stage_.Get()),
+ static_cast<ID3D11Resource*>(texture));
+
+ *rect() = {0};
+ _com_error error = surface_->Map(rect(), DXGI_MAP_READ);
+ if (error.Error() != S_OK) {
+ *rect() = {0};
+ RTC_LOG(LS_ERROR) << "Failed to map the IDXGISurface to a bitmap: "
+ << desktop_capture::utils::ComErrorToString(error);
+ return false;
+ }
+
+ return true;
+}
+
+bool DxgiTextureStaging::DoRelease() {
+ _com_error error = surface_->Unmap();
+ if (error.Error() != S_OK) {
+ stage_.Reset();
+ surface_.Reset();
+ }
+ // If using staging mode, we only need to recreate ID3D11Texture2D instance.
+ // This will happen during next CopyFrom call. So this function always returns
+ // true.
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.h b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.h
new file mode 100644
index 0000000000..e8c2af6662
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/dxgi_texture_staging.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_STAGING_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_STAGING_H_
+
+#include <d3d11.h>
+#include <dxgi1_2.h>
+#include <wrl/client.h>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/win/d3d_device.h"
+#include "modules/desktop_capture/win/dxgi_texture.h"
+
+namespace webrtc {
+
+// A pair of an ID3D11Texture2D and an IDXGISurface. We need an ID3D11Texture2D
+// instance to copy GPU texture to RAM, but an IDXGISurface instance to map the
+// texture into a bitmap buffer. These two instances are pointing to a same
+// object.
+//
+// An ID3D11Texture2D is created by an ID3D11Device, so a DxgiTexture cannot be
+// shared between two DxgiAdapterDuplicators.
+class DxgiTextureStaging : public DxgiTexture {
+ public:
+ // Creates a DxgiTextureStaging instance. Caller must maintain the lifetime
+ // of input device to make sure it outlives this instance.
+ explicit DxgiTextureStaging(const D3dDevice& device);
+
+ ~DxgiTextureStaging() override;
+
+ protected:
+ // Copies selected regions of a frame represented by frame_info and texture.
+ // Returns false if anything wrong.
+ bool CopyFromTexture(const DXGI_OUTDUPL_FRAME_INFO& frame_info,
+ ID3D11Texture2D* texture) override;
+
+ bool DoRelease() override;
+
+ private:
+ // Initializes stage_ from a CPU inaccessible IDXGIResource. Returns false if
+ // it failed to execute Windows APIs, or the size of the texture is not
+ // consistent with desktop_rect.
+ bool InitializeStage(ID3D11Texture2D* texture);
+
+ // Makes sure stage_ and surface_ are always pointing to a same object.
+ // We need an ID3D11Texture2D instance for
+ // ID3D11DeviceContext::CopySubresourceRegion, but an IDXGISurface for
+ // IDXGISurface::Map.
+ void AssertStageAndSurfaceAreSameObject();
+
+ const DesktopRect desktop_rect_;
+ const D3dDevice device_;
+ Microsoft::WRL::ComPtr<ID3D11Texture2D> stage_;
+ Microsoft::WRL::ComPtr<IDXGISurface> surface_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_DXGI_TEXTURE_STAGING_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.cc b/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.cc
new file mode 100644
index 0000000000..c6143ef785
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.cc
@@ -0,0 +1,296 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/full_screen_win_application_handler.h"
+
+#include <algorithm>
+#if defined(WEBRTC_MOZILLA_BUILD)
+#include <cwctype>
+#endif
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "absl/strings/ascii.h"
+#include "absl/strings/match.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "modules/desktop_capture/win/window_capture_utils.h"
+#include "rtc_base/arraysize.h"
+#include "rtc_base/logging.h" // For RTC_LOG_GLE
+#include "rtc_base/string_utils.h"
+
+namespace webrtc {
+namespace {
+
+// Utility function to verify that `window` has class name equal to `class_name`
+bool CheckWindowClassName(HWND window, const wchar_t* class_name) {
+ const size_t classNameLength = wcslen(class_name);
+
+ // https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassa
+ // says lpszClassName field in WNDCLASS is limited by 256 symbols, so we don't
+ // need to have a buffer bigger than that.
+ constexpr size_t kMaxClassNameLength = 256;
+ WCHAR buffer[kMaxClassNameLength];
+
+ const int length = ::GetClassNameW(window, buffer, kMaxClassNameLength);
+ if (length <= 0)
+ return false;
+
+ if (static_cast<size_t>(length) != classNameLength)
+ return false;
+ return wcsncmp(buffer, class_name, classNameLength) == 0;
+}
+
+std::string WindowText(HWND window) {
+ size_t len = ::GetWindowTextLength(window);
+ if (len == 0)
+ return std::string();
+
+ std::vector<wchar_t> buffer(len + 1, 0);
+ size_t copied = ::GetWindowTextW(window, buffer.data(), buffer.size());
+ if (copied == 0)
+ return std::string();
+ return rtc::ToUtf8(buffer.data(), copied);
+}
+
+DWORD WindowProcessId(HWND window) {
+ DWORD dwProcessId = 0;
+ ::GetWindowThreadProcessId(window, &dwProcessId);
+ return dwProcessId;
+}
+
+std::wstring FileNameFromPath(const std::wstring& path) {
+ auto found = path.rfind(L"\\");
+ if (found == std::string::npos)
+ return path;
+ return path.substr(found + 1);
+}
+
+// Returns windows which belong to given process id
+// `sources` is a full list of available windows
+// `processId` is a process identifier (window owner)
+// `window_to_exclude` is a window to be exluded from result
+DesktopCapturer::SourceList GetProcessWindows(
+ const DesktopCapturer::SourceList& sources,
+ DWORD processId,
+ HWND window_to_exclude) {
+ DesktopCapturer::SourceList result;
+ std::copy_if(sources.begin(), sources.end(), std::back_inserter(result),
+ [&](DesktopCapturer::Source source) {
+ const HWND source_hwnd = reinterpret_cast<HWND>(source.id);
+ return window_to_exclude != source_hwnd &&
+ WindowProcessId(source_hwnd) == processId;
+ });
+ return result;
+}
+
+class FullScreenPowerPointHandler : public FullScreenApplicationHandler {
+ public:
+ explicit FullScreenPowerPointHandler(DesktopCapturer::SourceId sourceId)
+ : FullScreenApplicationHandler(sourceId) {}
+
+ ~FullScreenPowerPointHandler() override {}
+
+ DesktopCapturer::SourceId FindFullScreenWindow(
+ const DesktopCapturer::SourceList& window_list,
+ int64_t timestamp) const override {
+ if (window_list.empty())
+ return 0;
+
+ HWND original_window = reinterpret_cast<HWND>(GetSourceId());
+ DWORD process_id = WindowProcessId(original_window);
+
+ DesktopCapturer::SourceList powerpoint_windows =
+ GetProcessWindows(window_list, process_id, original_window);
+
+ if (powerpoint_windows.empty())
+ return 0;
+
+ if (GetWindowType(original_window) != WindowType::kEditor)
+ return 0;
+
+ const auto original_document = GetDocumentFromEditorTitle(original_window);
+
+ for (const auto& source : powerpoint_windows) {
+ HWND window = reinterpret_cast<HWND>(source.id);
+
+ // Looking for slide show window for the same document
+ if (GetWindowType(window) != WindowType::kSlideShow ||
+ GetDocumentFromSlideShowTitle(window) != original_document) {
+ continue;
+ }
+
+ return source.id;
+ }
+
+ return 0;
+ }
+
+ private:
+ enum class WindowType { kEditor, kSlideShow, kOther };
+
+ WindowType GetWindowType(HWND window) const {
+ if (IsEditorWindow(window))
+ return WindowType::kEditor;
+ else if (IsSlideShowWindow(window))
+ return WindowType::kSlideShow;
+ else
+ return WindowType::kOther;
+ }
+
+ constexpr static char kDocumentTitleSeparator[] = " - ";
+
+ std::string GetDocumentFromEditorTitle(HWND window) const {
+ std::string title = WindowText(window);
+ auto position = title.find(kDocumentTitleSeparator);
+ return std::string(absl::StripAsciiWhitespace(
+ absl::string_view(title).substr(0, position)));
+ }
+
+ std::string GetDocumentFromSlideShowTitle(HWND window) const {
+ std::string title = WindowText(window);
+ auto left_pos = title.find(kDocumentTitleSeparator);
+ auto right_pos = title.rfind(kDocumentTitleSeparator);
+ constexpr size_t kSeparatorLength = arraysize(kDocumentTitleSeparator) - 1;
+ if (left_pos == std::string::npos || right_pos == std::string::npos)
+ return title;
+
+ if (right_pos > left_pos + kSeparatorLength) {
+ auto result_len = right_pos - left_pos - kSeparatorLength;
+ auto document = absl::string_view(title).substr(
+ left_pos + kSeparatorLength, result_len);
+ return std::string(absl::StripAsciiWhitespace(document));
+ } else {
+ auto document = absl::string_view(title).substr(
+ left_pos + kSeparatorLength, std::wstring::npos);
+ return std::string(absl::StripAsciiWhitespace(document));
+ }
+ }
+
+ bool IsEditorWindow(HWND window) const {
+ return CheckWindowClassName(window, L"PPTFrameClass");
+ }
+
+ bool IsSlideShowWindow(HWND window) const {
+ const LONG style = ::GetWindowLong(window, GWL_STYLE);
+ const bool min_box = WS_MINIMIZEBOX & style;
+ const bool max_box = WS_MAXIMIZEBOX & style;
+ return !min_box && !max_box;
+ }
+};
+
+class OpenOfficeApplicationHandler : public FullScreenApplicationHandler {
+ public:
+ explicit OpenOfficeApplicationHandler(DesktopCapturer::SourceId sourceId)
+ : FullScreenApplicationHandler(sourceId) {}
+
+ DesktopCapturer::SourceId FindFullScreenWindow(
+ const DesktopCapturer::SourceList& window_list,
+ int64_t timestamp) const override {
+ if (window_list.empty())
+ return 0;
+
+ DWORD process_id = WindowProcessId(reinterpret_cast<HWND>(GetSourceId()));
+
+ DesktopCapturer::SourceList app_windows =
+ GetProcessWindows(window_list, process_id, nullptr);
+
+ DesktopCapturer::SourceList document_windows;
+ std::copy_if(
+ app_windows.begin(), app_windows.end(),
+ std::back_inserter(document_windows),
+ [this](const DesktopCapturer::Source& x) { return IsEditorWindow(x); });
+
+ // Check if we have only one document window, otherwise it's not possible
+ // to securely match a document window and a slide show window which has
+ // empty title.
+ if (document_windows.size() != 1) {
+ return 0;
+ }
+
+ // Check if document window has been selected as a source
+ if (document_windows.front().id != GetSourceId()) {
+ return 0;
+ }
+
+ // Check if we have a slide show window.
+ auto slide_show_window =
+ std::find_if(app_windows.begin(), app_windows.end(),
+ [this](const DesktopCapturer::Source& x) {
+ return IsSlideShowWindow(x);
+ });
+
+ if (slide_show_window == app_windows.end())
+ return 0;
+
+ return slide_show_window->id;
+ }
+
+ private:
+ bool IsEditorWindow(const DesktopCapturer::Source& source) const {
+ if (source.title.empty()) {
+ return false;
+ }
+
+ return CheckWindowClassName(reinterpret_cast<HWND>(source.id), L"SALFRAME");
+ }
+
+ bool IsSlideShowWindow(const DesktopCapturer::Source& source) const {
+ // Check title size to filter out a Presenter Control window which shares
+ // window class with Slide Show window but has non empty title.
+ if (!source.title.empty()) {
+ return false;
+ }
+
+ return CheckWindowClassName(reinterpret_cast<HWND>(source.id),
+ L"SALTMPSUBFRAME");
+ }
+};
+
+std::wstring GetPathByWindowId(HWND window_id) {
+ DWORD process_id = WindowProcessId(window_id);
+ HANDLE process =
+ ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, process_id);
+ if (process == NULL)
+ return L"";
+ DWORD path_len = MAX_PATH;
+ WCHAR path[MAX_PATH];
+ std::wstring result;
+ if (::QueryFullProcessImageNameW(process, 0, path, &path_len))
+ result = std::wstring(path, path_len);
+ else
+ RTC_LOG_GLE(LS_ERROR) << "QueryFullProcessImageName failed.";
+
+ ::CloseHandle(process);
+ return result;
+}
+
+} // namespace
+
+std::unique_ptr<FullScreenApplicationHandler>
+CreateFullScreenWinApplicationHandler(DesktopCapturer::SourceId source_id) {
+ std::unique_ptr<FullScreenApplicationHandler> result;
+ HWND hwnd = reinterpret_cast<HWND>(source_id);
+ std::wstring exe_path = GetPathByWindowId(hwnd);
+ std::wstring file_name = FileNameFromPath(exe_path);
+ std::transform(file_name.begin(), file_name.end(), file_name.begin(),
+ std::towupper);
+
+ if (file_name == L"POWERPNT.EXE") {
+ result = std::make_unique<FullScreenPowerPointHandler>(source_id);
+ } else if (file_name == L"SOFFICE.BIN" &&
+ absl::EndsWith(WindowText(hwnd), "OpenOffice Impress")) {
+ result = std::make_unique<OpenOfficeApplicationHandler>(source_id);
+ }
+
+ return result;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.h b/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.h
new file mode 100644
index 0000000000..c97cbe252b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/full_screen_win_application_handler.h
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_FULL_SCREEN_WIN_APPLICATION_HANDLER_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_FULL_SCREEN_WIN_APPLICATION_HANDLER_H_
+
+#include <memory>
+#include "modules/desktop_capture/full_screen_application_handler.h"
+
+namespace webrtc {
+
+std::unique_ptr<FullScreenApplicationHandler>
+CreateFullScreenWinApplicationHandler(DesktopCapturer::SourceId sourceId);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_FULL_SCREEN_WIN_APPLICATION_HANDLER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/scoped_gdi_object.h b/third_party/libwebrtc/modules/desktop_capture/win/scoped_gdi_object.h
new file mode 100644
index 0000000000..2b01941e20
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/scoped_gdi_object.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_SCOPED_GDI_HANDLE_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_SCOPED_GDI_HANDLE_H_
+
+#include <windows.h>
+
+namespace webrtc {
+namespace win {
+
+// Scoper for GDI objects.
+template <class T, class Traits>
+class ScopedGDIObject {
+ public:
+ ScopedGDIObject() : handle_(NULL) {}
+ explicit ScopedGDIObject(T object) : handle_(object) {}
+
+ ~ScopedGDIObject() { Traits::Close(handle_); }
+
+ ScopedGDIObject(const ScopedGDIObject&) = delete;
+ ScopedGDIObject& operator=(const ScopedGDIObject&) = delete;
+
+ T Get() { return handle_; }
+
+ void Set(T object) {
+ if (handle_ && object != handle_)
+ Traits::Close(handle_);
+ handle_ = object;
+ }
+
+ ScopedGDIObject& operator=(T object) {
+ Set(object);
+ return *this;
+ }
+
+ T release() {
+ T object = handle_;
+ handle_ = NULL;
+ return object;
+ }
+
+ operator T() { return handle_; }
+
+ private:
+ T handle_;
+};
+
+// The traits class that uses DeleteObject() to close a handle.
+template <typename T>
+class DeleteObjectTraits {
+ public:
+ DeleteObjectTraits() = delete;
+ DeleteObjectTraits(const DeleteObjectTraits&) = delete;
+ DeleteObjectTraits& operator=(const DeleteObjectTraits&) = delete;
+
+ // Closes the handle.
+ static void Close(T handle) {
+ if (handle)
+ DeleteObject(handle);
+ }
+};
+
+// The traits class that uses DestroyCursor() to close a handle.
+class DestroyCursorTraits {
+ public:
+ DestroyCursorTraits() = delete;
+ DestroyCursorTraits(const DestroyCursorTraits&) = delete;
+ DestroyCursorTraits& operator=(const DestroyCursorTraits&) = delete;
+
+ // Closes the handle.
+ static void Close(HCURSOR handle) {
+ if (handle)
+ DestroyCursor(handle);
+ }
+};
+
+typedef ScopedGDIObject<HBITMAP, DeleteObjectTraits<HBITMAP> > ScopedBitmap;
+typedef ScopedGDIObject<HCURSOR, DestroyCursorTraits> ScopedCursor;
+
+} // namespace win
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_SCOPED_GDI_HANDLE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.cc b/third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.cc
new file mode 100644
index 0000000000..22e8e7bc8f
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.cc
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/scoped_thread_desktop.h"
+
+#include "modules/desktop_capture/win/desktop.h"
+
+namespace webrtc {
+
+ScopedThreadDesktop::ScopedThreadDesktop()
+ : initial_(Desktop::GetThreadDesktop()) {}
+
+ScopedThreadDesktop::~ScopedThreadDesktop() {
+ Revert();
+}
+
+bool ScopedThreadDesktop::IsSame(const Desktop& desktop) {
+ if (assigned_.get() != NULL) {
+ return assigned_->IsSame(desktop);
+ } else {
+ return initial_->IsSame(desktop);
+ }
+}
+
+void ScopedThreadDesktop::Revert() {
+ if (assigned_.get() != NULL) {
+ initial_->SetThreadDesktop();
+ assigned_.reset();
+ }
+}
+
+bool ScopedThreadDesktop::SetThreadDesktop(Desktop* desktop) {
+ Revert();
+
+ std::unique_ptr<Desktop> scoped_desktop(desktop);
+
+ if (initial_->IsSame(*desktop))
+ return true;
+
+ if (!desktop->SetThreadDesktop())
+ return false;
+
+ assigned_.reset(scoped_desktop.release());
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.h b/third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.h
new file mode 100644
index 0000000000..98f151a46c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/scoped_thread_desktop.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_SCOPED_THREAD_DESKTOP_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_SCOPED_THREAD_DESKTOP_H_
+
+#include <windows.h>
+
+#include <memory>
+
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+class Desktop;
+
+class RTC_EXPORT ScopedThreadDesktop {
+ public:
+ ScopedThreadDesktop();
+ ~ScopedThreadDesktop();
+
+ ScopedThreadDesktop(const ScopedThreadDesktop&) = delete;
+ ScopedThreadDesktop& operator=(const ScopedThreadDesktop&) = delete;
+
+ // Returns true if `desktop` has the same desktop name as the currently
+ // assigned desktop (if assigned) or as the initial desktop (if not assigned).
+ // Returns false in any other case including failing Win32 APIs and
+ // uninitialized desktop handles.
+ bool IsSame(const Desktop& desktop);
+
+ // Reverts the calling thread to use the initial desktop.
+ void Revert();
+
+ // Assigns `desktop` to be the calling thread. Returns true if the thread has
+ // been switched to `desktop` successfully. Takes ownership of `desktop`.
+ bool SetThreadDesktop(Desktop* desktop);
+
+ private:
+ // The desktop handle assigned to the calling thread by Set
+ std::unique_ptr<Desktop> assigned_;
+
+ // The desktop handle assigned to the calling thread at creation.
+ std::unique_ptr<Desktop> initial_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_SCOPED_THREAD_DESKTOP_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.cc b/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.cc
new file mode 100644
index 0000000000..1dc2918d08
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.cc
@@ -0,0 +1,184 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+
+#include <windows.h>
+
+#include <string>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/string_utils.h"
+#include "rtc_base/win32.h"
+
+namespace webrtc {
+
+bool HasActiveDisplay() {
+ DesktopCapturer::SourceList screens;
+
+ return GetScreenList(&screens) && !screens.empty();
+}
+
+bool GetScreenList(DesktopCapturer::SourceList* screens,
+ std::vector<std::string>* device_names /* = nullptr */) {
+ RTC_DCHECK(screens->empty());
+ RTC_DCHECK(!device_names || device_names->empty());
+
+ BOOL enum_result = TRUE;
+ for (int device_index = 0;; ++device_index) {
+ DISPLAY_DEVICEW device;
+ device.cb = sizeof(device);
+ enum_result = EnumDisplayDevicesW(NULL, device_index, &device, 0);
+
+ // `enum_result` is 0 if we have enumerated all devices.
+ if (!enum_result) {
+ break;
+ }
+
+ // We only care about active displays.
+ if (!(device.StateFlags & DISPLAY_DEVICE_ACTIVE)) {
+ continue;
+ }
+
+ screens->push_back({device_index, 0, std::string()});
+ if (device_names) {
+ device_names->push_back(rtc::ToUtf8(device.DeviceName));
+ }
+ }
+ return true;
+}
+
+bool GetHmonitorFromDeviceIndex(const DesktopCapturer::SourceId device_index,
+ HMONITOR* hmonitor) {
+ // A device index of `kFullDesktopScreenId` or -1 represents all screens, an
+ // HMONITOR of 0 indicates the same.
+ if (device_index == kFullDesktopScreenId) {
+ *hmonitor = 0;
+ return true;
+ }
+
+ std::wstring device_key;
+ if (!IsScreenValid(device_index, &device_key)) {
+ return false;
+ }
+
+ DesktopRect screen_rect = GetScreenRect(device_index, device_key);
+ if (screen_rect.is_empty()) {
+ return false;
+ }
+
+ RECT rect = {screen_rect.left(), screen_rect.top(), screen_rect.right(),
+ screen_rect.bottom()};
+
+ HMONITOR monitor = MonitorFromRect(&rect, MONITOR_DEFAULTTONULL);
+ if (monitor == NULL) {
+ RTC_LOG(LS_WARNING) << "No HMONITOR found for supplied device index.";
+ return false;
+ }
+
+ *hmonitor = monitor;
+ return true;
+}
+
+bool IsMonitorValid(const HMONITOR monitor) {
+ // An HMONITOR of 0 refers to a virtual monitor that spans all physical
+ // monitors.
+ if (monitor == 0) {
+ // There is a bug in a Windows OS API that causes a crash when capturing if
+ // there are no active displays. We must ensure there is an active display
+ // before returning true.
+ if (!HasActiveDisplay())
+ return false;
+
+ return true;
+ }
+
+ MONITORINFO monitor_info;
+ monitor_info.cbSize = sizeof(MONITORINFO);
+ return GetMonitorInfoA(monitor, &monitor_info);
+}
+
+DesktopRect GetMonitorRect(const HMONITOR monitor) {
+ MONITORINFO monitor_info;
+ monitor_info.cbSize = sizeof(MONITORINFO);
+ if (!GetMonitorInfoA(monitor, &monitor_info)) {
+ return DesktopRect();
+ }
+
+ return DesktopRect::MakeLTRB(
+ monitor_info.rcMonitor.left, monitor_info.rcMonitor.top,
+ monitor_info.rcMonitor.right, monitor_info.rcMonitor.bottom);
+}
+
+bool IsScreenValid(const DesktopCapturer::SourceId screen,
+ std::wstring* device_key) {
+ if (screen == kFullDesktopScreenId) {
+ *device_key = L"";
+ return true;
+ }
+
+ DISPLAY_DEVICEW device;
+ device.cb = sizeof(device);
+ BOOL enum_result = EnumDisplayDevicesW(NULL, screen, &device, 0);
+ if (enum_result) {
+ *device_key = device.DeviceKey;
+ }
+
+ return !!enum_result;
+}
+
+DesktopRect GetFullscreenRect() {
+ return DesktopRect::MakeXYWH(GetSystemMetrics(SM_XVIRTUALSCREEN),
+ GetSystemMetrics(SM_YVIRTUALSCREEN),
+ GetSystemMetrics(SM_CXVIRTUALSCREEN),
+ GetSystemMetrics(SM_CYVIRTUALSCREEN));
+}
+
+DesktopRect GetScreenRect(const DesktopCapturer::SourceId screen,
+ const std::wstring& device_key) {
+ RTC_DCHECK(IsGUIThread(false));
+ if (screen == kFullDesktopScreenId) {
+ return GetFullscreenRect();
+ }
+
+ DISPLAY_DEVICEW device;
+ device.cb = sizeof(device);
+ BOOL result = EnumDisplayDevicesW(NULL, screen, &device, 0);
+ if (!result) {
+ return DesktopRect();
+ }
+
+ // Verifies the device index still maps to the same display device, to make
+ // sure we are capturing the same device when devices are added or removed.
+ // DeviceKey is documented as reserved, but it actually contains the registry
+ // key for the device and is unique for each monitor, while DeviceID is not.
+ if (device_key != device.DeviceKey) {
+ return DesktopRect();
+ }
+
+ DEVMODEW device_mode;
+ device_mode.dmSize = sizeof(device_mode);
+ device_mode.dmDriverExtra = 0;
+ result = EnumDisplaySettingsExW(device.DeviceName, ENUM_CURRENT_SETTINGS,
+ &device_mode, 0);
+ if (!result) {
+ return DesktopRect();
+ }
+
+ return DesktopRect::MakeXYWH(
+ device_mode.dmPosition.x, device_mode.dmPosition.y,
+ device_mode.dmPelsWidth, device_mode.dmPelsHeight);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.h b/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.h
new file mode 100644
index 0000000000..97bfe816d8
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURE_UTILS_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURE_UTILS_H_
+
+#if defined(WEBRTC_WIN)
+// Forward declare HMONITOR in a windows.h compatible way so that we can avoid
+// including windows.h.
+#define WEBRTC_DECLARE_HANDLE(name) \
+struct name##__; \
+typedef struct name##__* name
+WEBRTC_DECLARE_HANDLE(HMONITOR);
+#undef WEBRTC_DECLARE_HANDLE
+#endif
+
+#include <string>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// Returns true if the system has at least one active display.
+bool HasActiveDisplay();
+
+// Output the list of active screens into `screens`. Returns true if succeeded,
+// or false if it fails to enumerate the display devices. If the `device_names`
+// is provided, it will be filled with the DISPLAY_DEVICE.DeviceName in UTF-8
+// encoding. If this function returns true, consumers can always assume that
+// `screens`[i] and `device_names`[i] indicate the same monitor on the system.
+bool GetScreenList(DesktopCapturer::SourceList* screens,
+ std::vector<std::string>* device_names = nullptr);
+
+// Converts a device index (which are returned by `GetScreenList`) into an
+// HMONITOR.
+bool GetHmonitorFromDeviceIndex(DesktopCapturer::SourceId device_index,
+ HMONITOR* hmonitor);
+
+// Returns true if `monitor` represents a valid display
+// monitor. Consumers should recheck the validity of HMONITORs before use if a
+// WM_DISPLAYCHANGE message has been received.
+bool IsMonitorValid(HMONITOR monitor);
+
+// Returns the rect of the monitor identified by `monitor`, relative to the
+// primary display's top-left. On failure, returns an empty rect.
+DesktopRect GetMonitorRect(HMONITOR monitor);
+
+// Returns true if `screen` is a valid screen. The screen device key is
+// returned through `device_key` if the screen is valid. The device key can be
+// used in GetScreenRect to verify the screen matches the previously obtained
+// id.
+bool IsScreenValid(DesktopCapturer::SourceId screen, std::wstring* device_key);
+
+// Get the rect of the entire system in system coordinate system. I.e. the
+// primary monitor always starts from (0, 0).
+DesktopRect GetFullscreenRect();
+
+// Get the rect of the screen identified by `screen`, relative to the primary
+// display's top-left. If the screen device key does not match `device_key`, or
+// the screen does not exist, or any error happens, an empty rect is returned.
+RTC_EXPORT DesktopRect GetScreenRect(DesktopCapturer::SourceId screen,
+ const std::wstring& device_key);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURE_UTILS_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils_unittest.cc
new file mode 100644
index 0000000000..2e58c6b164
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capture_utils_unittest.cc
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+
+#include <string>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "rtc_base/logging.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+TEST(ScreenCaptureUtilsTest, GetScreenList) {
+ DesktopCapturer::SourceList screens;
+ std::vector<std::string> device_names;
+
+ ASSERT_TRUE(GetScreenList(&screens));
+ screens.clear();
+ ASSERT_TRUE(GetScreenList(&screens, &device_names));
+
+ ASSERT_EQ(screens.size(), device_names.size());
+}
+
+TEST(ScreenCaptureUtilsTest, DeviceIndexToHmonitor) {
+ DesktopCapturer::SourceList screens;
+ ASSERT_TRUE(GetScreenList(&screens));
+ if (screens.empty()) {
+ RTC_LOG(LS_INFO)
+ << "Skip ScreenCaptureUtilsTest on systems with no monitors.";
+ GTEST_SKIP();
+ }
+
+ HMONITOR hmonitor;
+ ASSERT_TRUE(GetHmonitorFromDeviceIndex(screens[0].id, &hmonitor));
+ ASSERT_TRUE(IsMonitorValid(hmonitor));
+}
+
+TEST(ScreenCaptureUtilsTest, FullScreenDeviceIndexToHmonitor) {
+ if (!HasActiveDisplay()) {
+ RTC_LOG(LS_INFO)
+ << "Skip ScreenCaptureUtilsTest on systems with no monitors.";
+ GTEST_SKIP();
+ }
+
+ HMONITOR hmonitor;
+ ASSERT_TRUE(GetHmonitorFromDeviceIndex(kFullDesktopScreenId, &hmonitor));
+ ASSERT_EQ(hmonitor, static_cast<HMONITOR>(0));
+ ASSERT_TRUE(IsMonitorValid(hmonitor));
+}
+
+TEST(ScreenCaptureUtilsTest, NoMonitors) {
+ if (HasActiveDisplay()) {
+ RTC_LOG(LS_INFO) << "Skip ScreenCaptureUtilsTest designed specifically for "
+ "systems with no monitors";
+ GTEST_SKIP();
+ }
+
+ HMONITOR hmonitor;
+ ASSERT_TRUE(GetHmonitorFromDeviceIndex(kFullDesktopScreenId, &hmonitor));
+ ASSERT_EQ(hmonitor, static_cast<HMONITOR>(0));
+
+ // The monitor should be invalid since the system has no attached displays.
+ ASSERT_FALSE(IsMonitorValid(hmonitor));
+}
+
+TEST(ScreenCaptureUtilsTest, InvalidDeviceIndexToHmonitor) {
+ HMONITOR hmonitor;
+ ASSERT_FALSE(GetHmonitorFromDeviceIndex(kInvalidScreenId, &hmonitor));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc
new file mode 100644
index 0000000000..efa763993a
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.cc
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/screen_capturer_win_directx.h"
+
+#include <algorithm>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capture_metrics_helper.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/trace_event.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+
+using Microsoft::WRL::ComPtr;
+
+// static
+bool ScreenCapturerWinDirectx::IsSupported() {
+ // Forwards IsSupported() function call to DxgiDuplicatorController.
+ return DxgiDuplicatorController::Instance()->IsSupported();
+}
+
+// static
+bool ScreenCapturerWinDirectx::RetrieveD3dInfo(D3dInfo* info) {
+ // Forwards SupportedFeatureLevels() function call to
+ // DxgiDuplicatorController.
+ return DxgiDuplicatorController::Instance()->RetrieveD3dInfo(info);
+}
+
+// static
+bool ScreenCapturerWinDirectx::IsCurrentSessionSupported() {
+ return DxgiDuplicatorController::IsCurrentSessionSupported();
+}
+
+// static
+bool ScreenCapturerWinDirectx::GetScreenListFromDeviceNames(
+ const std::vector<std::string>& device_names,
+ DesktopCapturer::SourceList* screens) {
+ RTC_DCHECK(screens->empty());
+
+ DesktopCapturer::SourceList gdi_screens;
+ std::vector<std::string> gdi_names;
+ if (!GetScreenList(&gdi_screens, &gdi_names)) {
+ return false;
+ }
+
+ RTC_DCHECK_EQ(gdi_screens.size(), gdi_names.size());
+
+ ScreenId max_screen_id = -1;
+ for (const DesktopCapturer::Source& screen : gdi_screens) {
+ max_screen_id = std::max(max_screen_id, screen.id);
+ }
+
+ for (const auto& device_name : device_names) {
+ const auto it = std::find(gdi_names.begin(), gdi_names.end(), device_name);
+ if (it == gdi_names.end()) {
+ // devices_names[i] has not been found in gdi_names, so use max_screen_id.
+ max_screen_id++;
+ screens->push_back({max_screen_id});
+ } else {
+ screens->push_back({gdi_screens[it - gdi_names.begin()]});
+ }
+ }
+
+ return true;
+}
+
+// static
+int ScreenCapturerWinDirectx::GetIndexFromScreenId(
+ ScreenId id,
+ const std::vector<std::string>& device_names) {
+ DesktopCapturer::SourceList screens;
+ if (!GetScreenListFromDeviceNames(device_names, &screens)) {
+ return -1;
+ }
+
+ RTC_DCHECK_EQ(device_names.size(), screens.size());
+
+ for (size_t i = 0; i < screens.size(); i++) {
+ if (screens[i].id == id) {
+ return static_cast<int>(i);
+ }
+ }
+
+ return -1;
+}
+
+ScreenCapturerWinDirectx::ScreenCapturerWinDirectx()
+ : controller_(DxgiDuplicatorController::Instance()) {}
+
+ScreenCapturerWinDirectx::~ScreenCapturerWinDirectx() = default;
+
+void ScreenCapturerWinDirectx::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+ RecordCapturerImpl(DesktopCapturerId::kScreenCapturerWinDirectx);
+
+ callback_ = callback;
+}
+
+void ScreenCapturerWinDirectx::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
+ shared_memory_factory_ = std::move(shared_memory_factory);
+}
+
+void ScreenCapturerWinDirectx::CaptureFrame() {
+ RTC_DCHECK(callback_);
+ TRACE_EVENT0("webrtc", "ScreenCapturerWinDirectx::CaptureFrame");
+
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ // Note that the [] operator will create the ScreenCaptureFrameQueue if it
+ // doesn't exist, so this is safe.
+ ScreenCaptureFrameQueue<DxgiFrame>& frames =
+ frame_queue_map_[current_screen_id_];
+
+ frames.MoveToNextFrame();
+
+ if (!frames.current_frame()) {
+ frames.ReplaceCurrentFrame(
+ std::make_unique<DxgiFrame>(shared_memory_factory_.get()));
+ }
+
+ DxgiDuplicatorController::Result result;
+ if (current_screen_id_ == kFullDesktopScreenId) {
+ result = controller_->Duplicate(frames.current_frame());
+ } else {
+ result = controller_->DuplicateMonitor(frames.current_frame(),
+ current_screen_id_);
+ }
+
+ using DuplicateResult = DxgiDuplicatorController::Result;
+ if (result != DuplicateResult::SUCCEEDED) {
+ RTC_LOG(LS_ERROR) << "DxgiDuplicatorController failed to capture desktop, "
+ "error code "
+ << DxgiDuplicatorController::ResultName(result);
+ }
+ switch (result) {
+ case DuplicateResult::UNSUPPORTED_SESSION: {
+ RTC_LOG(LS_ERROR)
+ << "Current binary is running on a session not supported "
+ "by DirectX screen capturer.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ break;
+ }
+ case DuplicateResult::FRAME_PREPARE_FAILED: {
+ RTC_LOG(LS_ERROR) << "Failed to allocate a new DesktopFrame.";
+ // This usually means we do not have enough memory or SharedMemoryFactory
+ // cannot work correctly.
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ break;
+ }
+ case DuplicateResult::INVALID_MONITOR_ID: {
+ RTC_LOG(LS_ERROR) << "Invalid monitor id " << current_screen_id_;
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ break;
+ }
+ case DuplicateResult::INITIALIZATION_FAILED:
+ case DuplicateResult::DUPLICATION_FAILED: {
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ break;
+ }
+ case DuplicateResult::SUCCEEDED: {
+ std::unique_ptr<DesktopFrame> frame =
+ frames.current_frame()->frame()->Share();
+
+ int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec;
+ RTC_HISTOGRAM_COUNTS_1000(
+ "WebRTC.DesktopCapture.Win.DirectXCapturerFrameTime",
+ capture_time_ms);
+ frame->set_capture_time_ms(capture_time_ms);
+ frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinDirectx);
+
+ // TODO(julien.isorce): http://crbug.com/945468. Set the icc profile on
+ // the frame, see WindowCapturerMac::CaptureFrame.
+
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+ break;
+ }
+ }
+}
+
+bool ScreenCapturerWinDirectx::GetSourceList(SourceList* sources) {
+ std::vector<std::string> device_names;
+ if (!controller_->GetDeviceNames(&device_names)) {
+ return false;
+ }
+
+ return GetScreenListFromDeviceNames(device_names, sources);
+}
+
+bool ScreenCapturerWinDirectx::SelectSource(SourceId id) {
+ if (id == kFullDesktopScreenId) {
+ current_screen_id_ = id;
+ return true;
+ }
+
+ std::vector<std::string> device_names;
+ if (!controller_->GetDeviceNames(&device_names)) {
+ return false;
+ }
+
+ int index;
+ index = GetIndexFromScreenId(id, device_names);
+ if (index == -1) {
+ return false;
+ }
+
+ current_screen_id_ = index;
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.h b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.h
new file mode 100644
index 0000000000..801a0632fc
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_DIRECTX_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_DIRECTX_H_
+
+#include <d3dcommon.h>
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/win/dxgi_duplicator_controller.h"
+#include "modules/desktop_capture/win/dxgi_frame.h"
+#include "rtc_base/system/rtc_export.h"
+
+namespace webrtc {
+
+// ScreenCapturerWinDirectx captures 32bit RGBA using DirectX.
+class RTC_EXPORT ScreenCapturerWinDirectx : public DesktopCapturer {
+ public:
+ using D3dInfo = DxgiDuplicatorController::D3dInfo;
+
+ // Whether the system supports DirectX based capturing.
+ static bool IsSupported();
+
+ // Returns a most recent D3dInfo composed by
+ // DxgiDuplicatorController::Initialize() function. This function implicitly
+ // calls DxgiDuplicatorController::Initialize() if it has not been
+ // initialized. This function returns false and output parameter is kept
+ // unchanged if DxgiDuplicatorController::Initialize() failed.
+ // The D3dInfo may change based on hardware configuration even without
+ // restarting the hardware and software. Refer to https://goo.gl/OOCppq. So
+ // consumers should not cache the result returned by this function.
+ static bool RetrieveD3dInfo(D3dInfo* info);
+
+ // Whether current process is running in a Windows session which is supported
+ // by ScreenCapturerWinDirectx.
+ // Usually using ScreenCapturerWinDirectx in unsupported sessions will fail.
+ // But this behavior may vary on different Windows version. So consumers can
+ // always try IsSupported() function.
+ static bool IsCurrentSessionSupported();
+
+ // Maps `device_names` with the result from GetScreenList() and creates a new
+ // SourceList to include only the ones in `device_names`. If this function
+ // returns true, consumers can always assume `device_names`.size() equals to
+ // `screens`->size(), meanwhile `device_names`[i] and `screens`[i] indicate
+ // the same monitor on the system.
+ // Public for test only.
+ static bool GetScreenListFromDeviceNames(
+ const std::vector<std::string>& device_names,
+ DesktopCapturer::SourceList* screens);
+
+ // Maps `id` with the result from GetScreenListFromDeviceNames() and returns
+ // the index of the entity in `device_names`. This function returns -1 if `id`
+ // cannot be found.
+ // Public for test only.
+ static int GetIndexFromScreenId(ScreenId id,
+ const std::vector<std::string>& device_names);
+
+ explicit ScreenCapturerWinDirectx();
+
+ ~ScreenCapturerWinDirectx() override;
+
+ ScreenCapturerWinDirectx(const ScreenCapturerWinDirectx&) = delete;
+ ScreenCapturerWinDirectx& operator=(const ScreenCapturerWinDirectx&) = delete;
+
+ // DesktopCapturer implementation.
+ void Start(Callback* callback) override;
+ void SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+
+ private:
+ const rtc::scoped_refptr<DxgiDuplicatorController> controller_;
+
+ // The underlying DxgiDuplicators may retain a reference to the frames that
+ // we ask them to duplicate so that they can continue returning valid frames
+ // in the event that the target has not been updated. Thus, we need to ensure
+ // that we have a separate frame queue for each source id, so that these held
+ // frames don't get overwritten with the data from another Duplicator/monitor.
+ std::unordered_map<SourceId, ScreenCaptureFrameQueue<DxgiFrame>>
+ frame_queue_map_;
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;
+ Callback* callback_ = nullptr;
+ SourceId current_screen_id_ = kFullDesktopScreenId;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_DIRECTX_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx_unittest.cc
new file mode 100644
index 0000000000..c9f46f782c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_directx_unittest.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/screen_capturer_win_directx.h"
+
+#include <string>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+// This test cannot ensure GetScreenListFromDeviceNames() won't reorder the
+// devices in its output, since the device name is missing.
+TEST(ScreenCaptureUtilsTest, GetScreenListFromDeviceNamesAndGetIndex) {
+ const std::vector<std::string> device_names = {
+ "\\\\.\\DISPLAY0",
+ "\\\\.\\DISPLAY1",
+ "\\\\.\\DISPLAY2",
+ };
+ DesktopCapturer::SourceList screens;
+ ASSERT_TRUE(ScreenCapturerWinDirectx::GetScreenListFromDeviceNames(
+ device_names, &screens));
+ ASSERT_EQ(device_names.size(), screens.size());
+
+ for (size_t i = 0; i < screens.size(); i++) {
+ ASSERT_EQ(ScreenCapturerWinDirectx::GetIndexFromScreenId(screens[i].id,
+ device_names),
+ static_cast<int>(i));
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc
new file mode 100644
index 0000000000..4ab864b4b9
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.cc
@@ -0,0 +1,243 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/screen_capturer_win_gdi.h"
+
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capture_metrics_helper.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_frame_win.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/win/cursor.h"
+#include "modules/desktop_capture/win/desktop.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/trace_event.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+
+namespace {
+
+// Constants from dwmapi.h.
+const UINT DWM_EC_DISABLECOMPOSITION = 0;
+const UINT DWM_EC_ENABLECOMPOSITION = 1;
+
+const wchar_t kDwmapiLibraryName[] = L"dwmapi.dll";
+
+} // namespace
+
+ScreenCapturerWinGdi::ScreenCapturerWinGdi(
+ const DesktopCaptureOptions& options) {
+ if (options.disable_effects()) {
+ // Load dwmapi.dll dynamically since it is not available on XP.
+ if (!dwmapi_library_)
+ dwmapi_library_ = LoadLibraryW(kDwmapiLibraryName);
+
+ if (dwmapi_library_) {
+ composition_func_ = reinterpret_cast<DwmEnableCompositionFunc>(
+ GetProcAddress(dwmapi_library_, "DwmEnableComposition"));
+ composition_enabled_func_ = reinterpret_cast<DwmIsCompositionEnabledFunc>
+ (GetProcAddress(dwmapi_library_, "DwmIsCompositionEnabled"));
+ }
+ }
+}
+
+ScreenCapturerWinGdi::~ScreenCapturerWinGdi() {
+ if (desktop_dc_)
+ ReleaseDC(NULL, desktop_dc_);
+ if (memory_dc_)
+ DeleteDC(memory_dc_);
+
+ // Restore Aero.
+ if (composition_func_)
+ (*composition_func_)(DWM_EC_ENABLECOMPOSITION);
+
+ if (dwmapi_library_)
+ FreeLibrary(dwmapi_library_);
+}
+
+void ScreenCapturerWinGdi::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
+ shared_memory_factory_ = std::move(shared_memory_factory);
+}
+
+void ScreenCapturerWinGdi::CaptureFrame() {
+ TRACE_EVENT0("webrtc", "ScreenCapturerWinGdi::CaptureFrame");
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ queue_.MoveToNextFrame();
+ if (queue_.current_frame() && queue_.current_frame()->IsShared()) {
+ RTC_DLOG(LS_WARNING) << "Overwriting frame that is still shared.";
+ }
+
+ // Make sure the GDI capture resources are up-to-date.
+ PrepareCaptureResources();
+
+ if (!CaptureImage()) {
+ RTC_LOG(LS_WARNING) << "Failed to capture screen by GDI.";
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+
+ // Emit the current frame.
+ std::unique_ptr<DesktopFrame> frame = queue_.current_frame()->Share();
+ frame->set_dpi(DesktopVector(GetDeviceCaps(desktop_dc_, LOGPIXELSX),
+ GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
+ frame->mutable_updated_region()->SetRect(
+ DesktopRect::MakeSize(frame->size()));
+
+ int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec;
+ RTC_HISTOGRAM_COUNTS_1000(
+ "WebRTC.DesktopCapture.Win.ScreenGdiCapturerFrameTime", capture_time_ms);
+ frame->set_capture_time_ms(capture_time_ms);
+ frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinGdi);
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+}
+
+bool ScreenCapturerWinGdi::GetSourceList(SourceList* sources) {
+ return webrtc::GetScreenList(sources);
+}
+
+bool ScreenCapturerWinGdi::SelectSource(SourceId id) {
+ bool valid = IsScreenValid(id, &current_device_key_);
+ if (valid)
+ current_screen_id_ = id;
+ return valid;
+}
+
+void ScreenCapturerWinGdi::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+ RecordCapturerImpl(DesktopCapturerId::kScreenCapturerWinGdi);
+
+ callback_ = callback;
+
+ // Vote to disable Aero composited desktop effects while capturing. Windows
+ // will restore Aero automatically if the process exits. This has no effect
+ // under Windows 8 or higher. See crbug.com/124018.
+ if (composition_func_)
+ (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
+}
+
+void ScreenCapturerWinGdi::PrepareCaptureResources() {
+ // Switch to the desktop receiving user input if different from the current
+ // one.
+ std::unique_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
+ if (input_desktop && !desktop_.IsSame(*input_desktop)) {
+ // Release GDI resources otherwise SetThreadDesktop will fail.
+ if (desktop_dc_) {
+ ReleaseDC(NULL, desktop_dc_);
+ desktop_dc_ = nullptr;
+ }
+
+ if (memory_dc_) {
+ DeleteDC(memory_dc_);
+ memory_dc_ = nullptr;
+ }
+
+ // If SetThreadDesktop() fails, the thread is still assigned a desktop.
+ // So we can continue capture screen bits, just from the wrong desktop.
+ desktop_.SetThreadDesktop(input_desktop.release());
+
+ // Re-assert our vote to disable Aero.
+ // See crbug.com/124018 and crbug.com/129906.
+ if (composition_func_) {
+ (*composition_func_)(DWM_EC_DISABLECOMPOSITION);
+ }
+ }
+
+ // If the display configurations have changed then recreate GDI resources.
+ if (display_configuration_monitor_.IsChanged()) {
+ if (desktop_dc_) {
+ ReleaseDC(NULL, desktop_dc_);
+ desktop_dc_ = nullptr;
+ }
+ if (memory_dc_) {
+ DeleteDC(memory_dc_);
+ memory_dc_ = nullptr;
+ }
+ }
+
+ if (!desktop_dc_) {
+ RTC_DCHECK(!memory_dc_);
+
+ // Create GDI device contexts to capture from the desktop into memory.
+ desktop_dc_ = GetDC(nullptr);
+ RTC_CHECK(desktop_dc_);
+ memory_dc_ = CreateCompatibleDC(desktop_dc_);
+ RTC_CHECK(memory_dc_);
+
+ // Make sure the frame buffers will be reallocated.
+ queue_.Reset();
+ }
+}
+
+bool ScreenCapturerWinGdi::CaptureImage() {
+ RTC_DCHECK(IsGUIThread(false));
+ DesktopRect screen_rect =
+ GetScreenRect(current_screen_id_, current_device_key_);
+ if (screen_rect.is_empty()) {
+ RTC_LOG(LS_WARNING) << "Failed to get screen rect.";
+ return false;
+ }
+
+ DesktopSize size = screen_rect.size();
+ // If the current buffer is from an older generation then allocate a new one.
+ // Note that we can't reallocate other buffers at this point, since the caller
+ // may still be reading from them.
+ if (!queue_.current_frame() ||
+ !queue_.current_frame()->size().equals(screen_rect.size())) {
+ RTC_DCHECK(desktop_dc_);
+ RTC_DCHECK(memory_dc_);
+
+ std::unique_ptr<DesktopFrame> buffer = DesktopFrameWin::Create(
+ size, shared_memory_factory_.get(), desktop_dc_);
+ if (!buffer) {
+ RTC_LOG(LS_WARNING) << "Failed to create frame buffer.";
+ return false;
+ }
+ queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(buffer)));
+ }
+ queue_.current_frame()->set_top_left(
+ screen_rect.top_left().subtract(GetFullscreenRect().top_left()));
+
+ // Select the target bitmap into the memory dc and copy the rect from desktop
+ // to memory.
+ DesktopFrameWin* current = static_cast<DesktopFrameWin*>(
+ queue_.current_frame()->GetUnderlyingFrame());
+ HGDIOBJ previous_object = SelectObject(memory_dc_, current->bitmap());
+ if (!previous_object || previous_object == HGDI_ERROR) {
+ RTC_LOG(LS_WARNING) << "Failed to select current bitmap into memery dc.";
+ return false;
+ }
+
+ bool result = (BitBlt(memory_dc_, 0, 0, screen_rect.width(),
+ screen_rect.height(), desktop_dc_, screen_rect.left(),
+ screen_rect.top(), SRCCOPY | CAPTUREBLT) != FALSE);
+ if (!result) {
+ RTC_LOG_GLE(LS_WARNING) << "BitBlt failed";
+ }
+
+ // Select back the previously selected object to that the device contect
+ // could be destroyed independently of the bitmap if needed.
+ SelectObject(memory_dc_, previous_object);
+
+ return result;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h
new file mode 100644
index 0000000000..87c1ecfc8c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_gdi.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_
+
+#include <windows.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "modules/desktop_capture/win/display_configuration_monitor.h"
+#include "modules/desktop_capture/win/scoped_thread_desktop.h"
+
+namespace webrtc {
+
+// ScreenCapturerWinGdi captures 32bit RGB using GDI.
+//
+// ScreenCapturerWinGdi is double-buffered as required by ScreenCapturer.
+// This class does not detect DesktopFrame::updated_region(), the field is
+// always set to the entire frame rectangle. ScreenCapturerDifferWrapper should
+// be used if that functionality is necessary.
+class ScreenCapturerWinGdi : public DesktopCapturer {
+ public:
+ explicit ScreenCapturerWinGdi(const DesktopCaptureOptions& options);
+ ~ScreenCapturerWinGdi() override;
+
+ ScreenCapturerWinGdi(const ScreenCapturerWinGdi&) = delete;
+ ScreenCapturerWinGdi& operator=(const ScreenCapturerWinGdi&) = delete;
+
+ // Overridden from ScreenCapturer:
+ void Start(Callback* callback) override;
+ void SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+
+ private:
+ typedef HRESULT(WINAPI* DwmEnableCompositionFunc)(UINT);
+ typedef HRESULT(WINAPI* DwmIsCompositionEnabledFunc)(BOOL*);
+
+ // Make sure that the device contexts match the screen configuration.
+ void PrepareCaptureResources();
+
+ // Captures the current screen contents into the current buffer. Returns true
+ // if succeeded.
+ bool CaptureImage();
+
+ // Capture the current cursor shape.
+ void CaptureCursor();
+
+ Callback* callback_ = nullptr;
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;
+ SourceId current_screen_id_ = kFullDesktopScreenId;
+ std::wstring current_device_key_;
+
+ ScopedThreadDesktop desktop_;
+
+ // GDI resources used for screen capture.
+ HDC desktop_dc_ = NULL;
+ HDC memory_dc_ = NULL;
+
+ // Queue of the frames buffers.
+ ScreenCaptureFrameQueue<SharedDesktopFrame> queue_;
+
+ DisplayConfigurationMonitor display_configuration_monitor_;
+
+ HMODULE dwmapi_library_ = NULL;
+ DwmEnableCompositionFunc composition_func_ = nullptr;
+ DwmIsCompositionEnabledFunc composition_enabled_func_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_GDI_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc
new file mode 100644
index 0000000000..ce747e0141
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.cc
@@ -0,0 +1,398 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/screen_capturer_win_magnifier.h"
+
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capture_metrics_helper.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_frame_win.h"
+#include "modules/desktop_capture/desktop_region.h"
+#include "modules/desktop_capture/mouse_cursor.h"
+#include "modules/desktop_capture/win/cursor.h"
+#include "modules/desktop_capture/win/desktop.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/time_utils.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+
+namespace {
+DWORD GetTlsIndex() {
+ static const DWORD tls_index = TlsAlloc();
+ RTC_DCHECK(tls_index != TLS_OUT_OF_INDEXES);
+ return tls_index;
+}
+
+} // namespace
+
+// kMagnifierWindowClass has to be "Magnifier" according to the Magnification
+// API. The other strings can be anything.
+static wchar_t kMagnifierHostClass[] = L"ScreenCapturerWinMagnifierHost";
+static wchar_t kHostWindowName[] = L"MagnifierHost";
+static wchar_t kMagnifierWindowClass[] = L"Magnifier";
+static wchar_t kMagnifierWindowName[] = L"MagnifierWindow";
+
+ScreenCapturerWinMagnifier::ScreenCapturerWinMagnifier() = default;
+ScreenCapturerWinMagnifier::~ScreenCapturerWinMagnifier() {
+ // DestroyWindow must be called before MagUninitialize. magnifier_window_ is
+ // destroyed automatically when host_window_ is destroyed.
+ if (host_window_) {
+ DestroyWindow(host_window_);
+ host_window_ = NULL;
+ }
+
+ if (magnifier_initialized_) {
+ mag_uninitialize_func_();
+ magnifier_initialized_ = false;
+ }
+
+ if (mag_lib_handle_) {
+ FreeLibrary(mag_lib_handle_);
+ mag_lib_handle_ = NULL;
+ }
+
+ if (desktop_dc_) {
+ ReleaseDC(NULL, desktop_dc_);
+ desktop_dc_ = NULL;
+ }
+}
+
+void ScreenCapturerWinMagnifier::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+ RecordCapturerImpl(DesktopCapturerId::kScreenCapturerWinMagnifier);
+
+ callback_ = callback;
+
+ if (!InitializeMagnifier()) {
+ RTC_LOG_F(LS_WARNING) << "Magnifier initialization failed.";
+ }
+}
+
+void ScreenCapturerWinMagnifier::SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) {
+ shared_memory_factory_ = std::move(shared_memory_factory);
+}
+
+void ScreenCapturerWinMagnifier::CaptureFrame() {
+ RTC_DCHECK(callback_);
+ if (!magnifier_initialized_) {
+ RTC_LOG_F(LS_WARNING) << "Magnifier initialization failed.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ // Switch to the desktop receiving user input if different from the current
+ // one.
+ std::unique_ptr<Desktop> input_desktop(Desktop::GetInputDesktop());
+ if (input_desktop.get() != NULL && !desktop_.IsSame(*input_desktop)) {
+ // Release GDI resources otherwise SetThreadDesktop will fail.
+ if (desktop_dc_) {
+ ReleaseDC(NULL, desktop_dc_);
+ desktop_dc_ = NULL;
+ }
+ // If SetThreadDesktop() fails, the thread is still assigned a desktop.
+ // So we can continue capture screen bits, just from the wrong desktop.
+ desktop_.SetThreadDesktop(input_desktop.release());
+ }
+
+ DesktopRect rect = GetScreenRect(current_screen_id_, current_device_key_);
+ queue_.MoveToNextFrame();
+ CreateCurrentFrameIfNecessary(rect.size());
+ // CaptureImage may fail in some situations, e.g. windows8 metro mode. So
+ // defer to the fallback capturer if magnifier capturer did not work.
+ if (!CaptureImage(rect)) {
+ RTC_LOG_F(LS_WARNING) << "Magnifier capturer failed to capture a frame.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+
+ // Emit the current frame.
+ std::unique_ptr<DesktopFrame> frame = queue_.current_frame()->Share();
+ frame->set_dpi(DesktopVector(GetDeviceCaps(desktop_dc_, LOGPIXELSX),
+ GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
+ frame->mutable_updated_region()->SetRect(
+ DesktopRect::MakeSize(frame->size()));
+
+ int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec;
+ RTC_HISTOGRAM_COUNTS_1000(
+ "WebRTC.DesktopCapture.Win.MagnifierCapturerFrameTime", capture_time_ms);
+ frame->set_capture_time_ms(capture_time_ms);
+ frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinMagnifier);
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+}
+
+bool ScreenCapturerWinMagnifier::GetSourceList(SourceList* sources) {
+ return webrtc::GetScreenList(sources);
+}
+
+bool ScreenCapturerWinMagnifier::SelectSource(SourceId id) {
+ if (IsScreenValid(id, &current_device_key_)) {
+ current_screen_id_ = id;
+ return true;
+ }
+
+ return false;
+}
+
+void ScreenCapturerWinMagnifier::SetExcludedWindow(WindowId excluded_window) {
+ excluded_window_ = (HWND)excluded_window;
+ if (excluded_window_ && magnifier_initialized_) {
+ set_window_filter_list_func_(magnifier_window_, MW_FILTERMODE_EXCLUDE, 1,
+ &excluded_window_);
+ }
+}
+
+bool ScreenCapturerWinMagnifier::CaptureImage(const DesktopRect& rect) {
+ RTC_DCHECK(magnifier_initialized_);
+
+ // Set the magnifier control to cover the captured rect. The content of the
+ // magnifier control will be the captured image.
+ BOOL result = SetWindowPos(magnifier_window_, NULL, rect.left(), rect.top(),
+ rect.width(), rect.height(), 0);
+ if (!result) {
+ RTC_LOG_F(LS_WARNING) << "Failed to call SetWindowPos: " << GetLastError()
+ << ". Rect = {" << rect.left() << ", " << rect.top()
+ << ", " << rect.right() << ", " << rect.bottom()
+ << "}";
+ return false;
+ }
+
+ magnifier_capture_succeeded_ = false;
+
+ RECT native_rect = {rect.left(), rect.top(), rect.right(), rect.bottom()};
+
+ TlsSetValue(GetTlsIndex(), this);
+ // OnCaptured will be called via OnMagImageScalingCallback and fill in the
+ // frame before set_window_source_func_ returns.
+ result = set_window_source_func_(magnifier_window_, native_rect);
+
+ if (!result) {
+ RTC_LOG_F(LS_WARNING) << "Failed to call MagSetWindowSource: "
+ << GetLastError() << ". Rect = {" << rect.left()
+ << ", " << rect.top() << ", " << rect.right() << ", "
+ << rect.bottom() << "}";
+ return false;
+ }
+
+ return magnifier_capture_succeeded_;
+}
+
+BOOL ScreenCapturerWinMagnifier::OnMagImageScalingCallback(
+ HWND hwnd,
+ void* srcdata,
+ MAGIMAGEHEADER srcheader,
+ void* destdata,
+ MAGIMAGEHEADER destheader,
+ RECT unclipped,
+ RECT clipped,
+ HRGN dirty) {
+ ScreenCapturerWinMagnifier* owner =
+ reinterpret_cast<ScreenCapturerWinMagnifier*>(TlsGetValue(GetTlsIndex()));
+ TlsSetValue(GetTlsIndex(), nullptr);
+ owner->OnCaptured(srcdata, srcheader);
+
+ return TRUE;
+}
+
+// TODO(zijiehe): These functions are available on Windows Vista or upper, so we
+// do not need to use LoadLibrary and GetProcAddress anymore. Use regular
+// include and function calls instead of a dynamical loaded library.
+bool ScreenCapturerWinMagnifier::InitializeMagnifier() {
+ RTC_DCHECK(!magnifier_initialized_);
+
+ if (GetSystemMetrics(SM_CMONITORS) != 1) {
+ // Do not try to use the magnifier in multi-screen setup (where the API
+ // crashes sometimes).
+ RTC_LOG_F(LS_WARNING) << "Magnifier capturer cannot work on multi-screen "
+ "system.";
+ return false;
+ }
+
+ desktop_dc_ = GetDC(nullptr);
+
+ mag_lib_handle_ = LoadLibraryW(L"Magnification.dll");
+ if (!mag_lib_handle_)
+ return false;
+
+ // Initialize Magnification API function pointers.
+ mag_initialize_func_ = reinterpret_cast<MagInitializeFunc>(
+ GetProcAddress(mag_lib_handle_, "MagInitialize"));
+ mag_uninitialize_func_ = reinterpret_cast<MagUninitializeFunc>(
+ GetProcAddress(mag_lib_handle_, "MagUninitialize"));
+ set_window_source_func_ = reinterpret_cast<MagSetWindowSourceFunc>(
+ GetProcAddress(mag_lib_handle_, "MagSetWindowSource"));
+ set_window_filter_list_func_ = reinterpret_cast<MagSetWindowFilterListFunc>(
+ GetProcAddress(mag_lib_handle_, "MagSetWindowFilterList"));
+ set_image_scaling_callback_func_ =
+ reinterpret_cast<MagSetImageScalingCallbackFunc>(
+ GetProcAddress(mag_lib_handle_, "MagSetImageScalingCallback"));
+
+ if (!mag_initialize_func_ || !mag_uninitialize_func_ ||
+ !set_window_source_func_ || !set_window_filter_list_func_ ||
+ !set_image_scaling_callback_func_) {
+ RTC_LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ "library functions missing.";
+ return false;
+ }
+
+ BOOL result = mag_initialize_func_();
+ if (!result) {
+ RTC_LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ "error from MagInitialize "
+ << GetLastError();
+ return false;
+ }
+
+ HMODULE hInstance = nullptr;
+ result =
+ GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<char*>(&DefWindowProc), &hInstance);
+ if (!result) {
+ mag_uninitialize_func_();
+ RTC_LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ "error from GetModulehandleExA "
+ << GetLastError();
+ return false;
+ }
+
+ // Register the host window class. See the MSDN documentation of the
+ // Magnification API for more infomation.
+ WNDCLASSEXW wcex = {};
+ wcex.cbSize = sizeof(WNDCLASSEX);
+ wcex.lpfnWndProc = &DefWindowProc;
+ wcex.hInstance = hInstance;
+ wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wcex.lpszClassName = kMagnifierHostClass;
+
+ // Ignore the error which may happen when the class is already registered.
+ RegisterClassExW(&wcex);
+
+ // Create the host window.
+ host_window_ =
+ CreateWindowExW(WS_EX_LAYERED, kMagnifierHostClass, kHostWindowName, 0, 0,
+ 0, 0, 0, nullptr, nullptr, hInstance, nullptr);
+ if (!host_window_) {
+ mag_uninitialize_func_();
+ RTC_LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ "error from creating host window "
+ << GetLastError();
+ return false;
+ }
+
+ // Create the magnifier control.
+ magnifier_window_ = CreateWindowW(kMagnifierWindowClass, kMagnifierWindowName,
+ WS_CHILD | WS_VISIBLE, 0, 0, 0, 0,
+ host_window_, nullptr, hInstance, nullptr);
+ if (!magnifier_window_) {
+ mag_uninitialize_func_();
+ RTC_LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ "error from creating magnifier window "
+ << GetLastError();
+ return false;
+ }
+
+ // Hide the host window.
+ ShowWindow(host_window_, SW_HIDE);
+
+ // Set the scaling callback to receive captured image.
+ result = set_image_scaling_callback_func_(
+ magnifier_window_,
+ &ScreenCapturerWinMagnifier::OnMagImageScalingCallback);
+ if (!result) {
+ mag_uninitialize_func_();
+ RTC_LOG_F(LS_WARNING) << "Failed to initialize ScreenCapturerWinMagnifier: "
+ "error from MagSetImageScalingCallback "
+ << GetLastError();
+ return false;
+ }
+
+ if (excluded_window_) {
+ result = set_window_filter_list_func_(
+ magnifier_window_, MW_FILTERMODE_EXCLUDE, 1, &excluded_window_);
+ if (!result) {
+ mag_uninitialize_func_();
+ RTC_LOG_F(LS_WARNING)
+ << "Failed to initialize ScreenCapturerWinMagnifier: "
+ "error from MagSetWindowFilterList "
+ << GetLastError();
+ return false;
+ }
+ }
+
+ magnifier_initialized_ = true;
+ return true;
+}
+
+void ScreenCapturerWinMagnifier::OnCaptured(void* data,
+ const MAGIMAGEHEADER& header) {
+ DesktopFrame* current_frame = queue_.current_frame();
+
+ // Verify the format.
+ // TODO(jiayl): support capturing sources with pixel formats other than RGBA.
+ int captured_bytes_per_pixel = header.cbSize / header.width / header.height;
+ if (header.format != GUID_WICPixelFormat32bppRGBA ||
+ header.width != static_cast<UINT>(current_frame->size().width()) ||
+ header.height != static_cast<UINT>(current_frame->size().height()) ||
+ header.stride != static_cast<UINT>(current_frame->stride()) ||
+ captured_bytes_per_pixel != DesktopFrame::kBytesPerPixel) {
+ RTC_LOG_F(LS_WARNING)
+ << "Output format does not match the captured format: "
+ "width = "
+ << header.width
+ << ", "
+ "height = "
+ << header.height
+ << ", "
+ "stride = "
+ << header.stride
+ << ", "
+ "bpp = "
+ << captured_bytes_per_pixel
+ << ", "
+ "pixel format RGBA ? "
+ << (header.format == GUID_WICPixelFormat32bppRGBA) << ".";
+ return;
+ }
+
+ // Copy the data into the frame.
+ current_frame->CopyPixelsFrom(
+ reinterpret_cast<uint8_t*>(data), header.stride,
+ DesktopRect::MakeXYWH(0, 0, header.width, header.height));
+
+ magnifier_capture_succeeded_ = true;
+}
+
+void ScreenCapturerWinMagnifier::CreateCurrentFrameIfNecessary(
+ const DesktopSize& size) {
+ // If the current buffer is from an older generation then allocate a new one.
+ // Note that we can't reallocate other buffers at this point, since the caller
+ // may still be reading from them.
+ if (!queue_.current_frame() || !queue_.current_frame()->size().equals(size)) {
+ std::unique_ptr<DesktopFrame> frame =
+ shared_memory_factory_
+ ? SharedMemoryDesktopFrame::Create(size,
+ shared_memory_factory_.get())
+ : std::unique_ptr<DesktopFrame>(new BasicDesktopFrame(size));
+ queue_.ReplaceCurrentFrame(SharedDesktopFrame::Wrap(std::move(frame)));
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h
new file mode 100644
index 0000000000..07c5b1e9e6
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/screen_capturer_win_magnifier.h
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_
+
+#include <magnification.h>
+#include <wincodec.h>
+#include <windows.h>
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/screen_capture_frame_queue.h"
+#include "modules/desktop_capture/screen_capturer_helper.h"
+#include "modules/desktop_capture/shared_desktop_frame.h"
+#include "modules/desktop_capture/win/scoped_thread_desktop.h"
+
+namespace webrtc {
+
+class DesktopFrame;
+class DesktopRect;
+
+// Captures the screen using the Magnification API to support window exclusion.
+// Each capturer must run on a dedicated thread because it uses thread local
+// storage for redirecting the library callback. Also the thread must have a UI
+// message loop to handle the window messages for the magnifier window.
+//
+// This class does not detect DesktopFrame::updated_region(), the field is
+// always set to the entire frame rectangle. ScreenCapturerDifferWrapper should
+// be used if that functionality is necessary.
+class ScreenCapturerWinMagnifier : public DesktopCapturer {
+ public:
+ ScreenCapturerWinMagnifier();
+ ~ScreenCapturerWinMagnifier() override;
+
+ ScreenCapturerWinMagnifier(const ScreenCapturerWinMagnifier&) = delete;
+ ScreenCapturerWinMagnifier& operator=(const ScreenCapturerWinMagnifier&) =
+ delete;
+
+ // Overridden from ScreenCapturer:
+ void Start(Callback* callback) override;
+ void SetSharedMemoryFactory(
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* screens) override;
+ bool SelectSource(SourceId id) override;
+ void SetExcludedWindow(WindowId window) override;
+
+ private:
+ typedef BOOL(WINAPI* MagImageScalingCallback)(HWND hwnd,
+ void* srcdata,
+ MAGIMAGEHEADER srcheader,
+ void* destdata,
+ MAGIMAGEHEADER destheader,
+ RECT unclipped,
+ RECT clipped,
+ HRGN dirty);
+ typedef BOOL(WINAPI* MagInitializeFunc)(void);
+ typedef BOOL(WINAPI* MagUninitializeFunc)(void);
+ typedef BOOL(WINAPI* MagSetWindowSourceFunc)(HWND hwnd, RECT rect);
+ typedef BOOL(WINAPI* MagSetWindowFilterListFunc)(HWND hwnd,
+ DWORD dwFilterMode,
+ int count,
+ HWND* pHWND);
+ typedef BOOL(WINAPI* MagSetImageScalingCallbackFunc)(
+ HWND hwnd,
+ MagImageScalingCallback callback);
+
+ static BOOL WINAPI OnMagImageScalingCallback(HWND hwnd,
+ void* srcdata,
+ MAGIMAGEHEADER srcheader,
+ void* destdata,
+ MAGIMAGEHEADER destheader,
+ RECT unclipped,
+ RECT clipped,
+ HRGN dirty);
+
+ // Captures the screen within `rect` in the desktop coordinates. Returns true
+ // if succeeded.
+ // It can only capture the primary screen for now. The magnification library
+ // crashes under some screen configurations (e.g. secondary screen on top of
+ // primary screen) if it tries to capture a non-primary screen. The caller
+ // must make sure not calling it on non-primary screens.
+ bool CaptureImage(const DesktopRect& rect);
+
+ // Helper method for setting up the magnifier control. Returns true if
+ // succeeded.
+ bool InitializeMagnifier();
+
+ // Called by OnMagImageScalingCallback to output captured data.
+ void OnCaptured(void* data, const MAGIMAGEHEADER& header);
+
+ // Makes sure the current frame exists and matches `size`.
+ void CreateCurrentFrameIfNecessary(const DesktopSize& size);
+
+ Callback* callback_ = nullptr;
+ std::unique_ptr<SharedMemoryFactory> shared_memory_factory_;
+ ScreenId current_screen_id_ = kFullDesktopScreenId;
+ std::wstring current_device_key_;
+ HWND excluded_window_ = NULL;
+
+ // Queue of the frames buffers.
+ ScreenCaptureFrameQueue<SharedDesktopFrame> queue_;
+
+ ScopedThreadDesktop desktop_;
+
+ // Used for getting the screen dpi.
+ HDC desktop_dc_ = NULL;
+
+ HMODULE mag_lib_handle_ = NULL;
+ MagInitializeFunc mag_initialize_func_ = nullptr;
+ MagUninitializeFunc mag_uninitialize_func_ = nullptr;
+ MagSetWindowSourceFunc set_window_source_func_ = nullptr;
+ MagSetWindowFilterListFunc set_window_filter_list_func_ = nullptr;
+ MagSetImageScalingCallbackFunc set_image_scaling_callback_func_ = nullptr;
+
+ // The hidden window hosting the magnifier control.
+ HWND host_window_ = NULL;
+ // The magnifier control that captures the screen.
+ HWND magnifier_window_ = NULL;
+
+ // True if the magnifier control has been successfully initialized.
+ bool magnifier_initialized_ = false;
+
+ // True if the last OnMagImageScalingCallback was called and handled
+ // successfully. Reset at the beginning of each CaptureImage call.
+ bool magnifier_capture_succeeded_ = true;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_SCREEN_CAPTURER_WIN_MAGNIFIER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.cc b/third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.cc
new file mode 100644
index 0000000000..398ea1e53a
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.cc
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/selected_window_context.h"
+
+namespace webrtc {
+
+SelectedWindowContext::SelectedWindowContext(
+ HWND selected_window,
+ DesktopRect selected_window_rect,
+ WindowCaptureHelperWin* window_capture_helper)
+ : selected_window_(selected_window),
+ selected_window_rect_(selected_window_rect),
+ window_capture_helper_(window_capture_helper) {
+ selected_window_thread_id_ =
+ GetWindowThreadProcessId(selected_window, &selected_window_process_id_);
+}
+
+bool SelectedWindowContext::IsSelectedWindowValid() const {
+ return selected_window_thread_id_ != 0;
+}
+
+bool SelectedWindowContext::IsWindowOwnedBySelectedWindow(HWND hwnd) const {
+ // This check works for drop-down menus & dialog pop-up windows.
+ if (GetAncestor(hwnd, GA_ROOTOWNER) == selected_window_) {
+ return true;
+ }
+
+ // Assume that all other windows are unrelated to the selected window.
+ // This will cause some windows that are actually related to be missed,
+ // e.g. context menus and tool-tips, but avoids the risk of capturing
+ // unrelated windows. Using heuristics such as matching the thread and
+ // process Ids suffers from false-positives, e.g. in multi-document
+ // applications.
+
+ return false;
+}
+
+bool SelectedWindowContext::IsWindowOverlappingSelectedWindow(HWND hwnd) const {
+ return window_capture_helper_->AreWindowsOverlapping(hwnd, selected_window_,
+ selected_window_rect_);
+}
+
+HWND SelectedWindowContext::selected_window() const {
+ return selected_window_;
+}
+
+WindowCaptureHelperWin* SelectedWindowContext::window_capture_helper() const {
+ return window_capture_helper_;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.h b/third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.h
new file mode 100644
index 0000000000..99e38e3fa2
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/selected_window_context.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_SELECTED_WINDOW_CONTEXT_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_SELECTED_WINDOW_CONTEXT_H_
+
+#include <windows.h>
+
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/win/window_capture_utils.h"
+
+namespace webrtc {
+
+class SelectedWindowContext {
+ public:
+ SelectedWindowContext(HWND selected_window,
+ DesktopRect selected_window_rect,
+ WindowCaptureHelperWin* window_capture_helper);
+
+ bool IsSelectedWindowValid() const;
+
+ bool IsWindowOwnedBySelectedWindow(HWND hwnd) const;
+ bool IsWindowOverlappingSelectedWindow(HWND hwnd) const;
+
+ HWND selected_window() const;
+ WindowCaptureHelperWin* window_capture_helper() const;
+
+ private:
+ const HWND selected_window_;
+ const DesktopRect selected_window_rect_;
+ WindowCaptureHelperWin* const window_capture_helper_;
+ DWORD selected_window_thread_id_;
+ DWORD selected_window_process_id_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_SELECTED_WINDOW_CONTEXT_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.cc b/third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.cc
new file mode 100644
index 0000000000..c07ff74aa5
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/test_support/test_window.h"
+
+namespace webrtc {
+namespace {
+
+const WCHAR kWindowClass[] = L"DesktopCaptureTestWindowClass";
+const int kWindowHeight = 200;
+const int kWindowWidth = 300;
+
+LRESULT CALLBACK WindowProc(HWND hwnd,
+ UINT msg,
+ WPARAM w_param,
+ LPARAM l_param) {
+ switch (msg) {
+ case WM_PAINT:
+ PAINTSTRUCT paint_struct;
+ HDC hdc = BeginPaint(hwnd, &paint_struct);
+
+ // Paint the window so the color is consistent and we can inspect the
+ // pixels in tests and know what to expect.
+ FillRect(hdc, &paint_struct.rcPaint,
+ CreateSolidBrush(RGB(kTestWindowRValue, kTestWindowGValue,
+ kTestWindowBValue)));
+
+ EndPaint(hwnd, &paint_struct);
+ }
+ return DefWindowProc(hwnd, msg, w_param, l_param);
+}
+
+} // namespace
+
+WindowInfo CreateTestWindow(const WCHAR* window_title,
+ const int height,
+ const int width,
+ const LONG extended_styles) {
+ WindowInfo info;
+ ::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
+ GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
+ reinterpret_cast<LPCWSTR>(&WindowProc),
+ &info.window_instance);
+
+ WNDCLASSEXW wcex;
+ memset(&wcex, 0, sizeof(wcex));
+ wcex.cbSize = sizeof(wcex);
+ wcex.style = CS_HREDRAW | CS_VREDRAW;
+ wcex.hInstance = info.window_instance;
+ wcex.lpfnWndProc = &WindowProc;
+ wcex.lpszClassName = kWindowClass;
+ info.window_class = ::RegisterClassExW(&wcex);
+
+ // Use the default height and width if the caller did not supply the optional
+ // height and width parameters, or if they supplied invalid values.
+ int window_height = height <= 0 ? kWindowHeight : height;
+ int window_width = width <= 0 ? kWindowWidth : width;
+ info.hwnd =
+ ::CreateWindowExW(extended_styles, kWindowClass, window_title,
+ WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
+ window_width, window_height, /*parent_window=*/nullptr,
+ /*menu_bar=*/nullptr, info.window_instance,
+ /*additional_params=*/nullptr);
+
+ ::ShowWindow(info.hwnd, SW_SHOWNORMAL);
+ ::UpdateWindow(info.hwnd);
+ return info;
+}
+
+void ResizeTestWindow(const HWND hwnd, const int width, const int height) {
+ // SWP_NOMOVE results in the x and y params being ignored.
+ ::SetWindowPos(hwnd, HWND_TOP, /*x-coord=*/0, /*y-coord=*/0, width, height,
+ SWP_SHOWWINDOW | SWP_NOMOVE);
+ ::UpdateWindow(hwnd);
+}
+
+void MoveTestWindow(const HWND hwnd, const int x, const int y) {
+ // SWP_NOSIZE results in the width and height params being ignored.
+ ::SetWindowPos(hwnd, HWND_TOP, x, y, /*width=*/0, /*height=*/0,
+ SWP_SHOWWINDOW | SWP_NOSIZE);
+ ::UpdateWindow(hwnd);
+}
+
+void MinimizeTestWindow(const HWND hwnd) {
+ ::ShowWindow(hwnd, SW_MINIMIZE);
+}
+
+void UnminimizeTestWindow(const HWND hwnd) {
+ ::OpenIcon(hwnd);
+}
+
+void DestroyTestWindow(WindowInfo info) {
+ ::DestroyWindow(info.hwnd);
+ ::UnregisterClass(MAKEINTATOM(info.window_class), info.window_instance);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.h b/third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.h
new file mode 100644
index 0000000000..b055da7ccd
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/test_support/test_window.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_TEST_SUPPORT_TEST_WINDOW_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_TEST_SUPPORT_TEST_WINDOW_H_
+
+#ifndef WIN32_LEAN_AND_MEAN
+#define WIN32_LEAN_AND_MEAN
+#endif
+#include <windows.h>
+
+namespace webrtc {
+
+typedef unsigned char uint8_t;
+
+// Define an arbitrary color for the test window with unique R, G, and B values
+// so consumers can verify captured content in tests.
+const uint8_t kTestWindowRValue = 191;
+const uint8_t kTestWindowGValue = 99;
+const uint8_t kTestWindowBValue = 12;
+
+struct WindowInfo {
+ HWND hwnd;
+ HINSTANCE window_instance;
+ ATOM window_class;
+};
+
+WindowInfo CreateTestWindow(const WCHAR* window_title,
+ int height = 0,
+ int width = 0,
+ LONG extended_styles = 0);
+
+void ResizeTestWindow(HWND hwnd, int width, int height);
+
+void MoveTestWindow(HWND hwnd, int x, int y);
+
+void MinimizeTestWindow(HWND hwnd);
+
+void UnminimizeTestWindow(HWND hwnd);
+
+void DestroyTestWindow(WindowInfo info);
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_TEST_SUPPORT_TEST_WINDOW_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.cc b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.cc
new file mode 100644
index 0000000000..0ff2db27c7
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.cc
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/wgc_capture_session.h"
+
+#include <DispatcherQueue.h>
+#include <windows.graphics.capture.interop.h>
+#include <windows.graphics.directX.direct3d11.interop.h>
+#include <windows.graphics.h>
+#include <wrl/client.h>
+#include <wrl/event.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "modules/desktop_capture/win/wgc_desktop_frame.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/win/create_direct3d_device.h"
+#include "rtc_base/win/get_activation_factory.h"
+#include "system_wrappers/include/metrics.h"
+
+using Microsoft::WRL::ComPtr;
+namespace WGC = ABI::Windows::Graphics::Capture;
+
+namespace webrtc {
+namespace {
+
+// We must use a BGRA pixel format that has 4 bytes per pixel, as required by
+// the DesktopFrame interface.
+constexpr auto kPixelFormat = ABI::Windows::Graphics::DirectX::
+ DirectXPixelFormat::DirectXPixelFormat_B8G8R8A8UIntNormalized;
+
+// The maximum time `GetFrame` will wait for a frame to arrive, if we don't have
+// any in the pool.
+constexpr int kMaxWaitForFrameMs = 50;
+constexpr int kMaxWaitForFirstFrameMs = 500;
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class StartCaptureResult {
+ kSuccess = 0,
+ kSourceClosed = 1,
+ kAddClosedFailed = 2,
+ kDxgiDeviceCastFailed = 3,
+ kD3dDelayLoadFailed = 4,
+ kD3dDeviceCreationFailed = 5,
+ kFramePoolActivationFailed = 6,
+ // kFramePoolCastFailed = 7, (deprecated)
+ // kGetItemSizeFailed = 8, (deprecated)
+ kCreateFramePoolFailed = 9,
+ kCreateCaptureSessionFailed = 10,
+ kStartCaptureFailed = 11,
+ kMaxValue = kStartCaptureFailed
+};
+
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum class GetFrameResult {
+ kSuccess = 0,
+ kItemClosed = 1,
+ kTryGetNextFrameFailed = 2,
+ kFrameDropped = 3,
+ kGetSurfaceFailed = 4,
+ kDxgiInterfaceAccessFailed = 5,
+ kTexture2dCastFailed = 6,
+ kCreateMappedTextureFailed = 7,
+ kMapFrameFailed = 8,
+ kGetContentSizeFailed = 9,
+ kResizeMappedTextureFailed = 10,
+ kRecreateFramePoolFailed = 11,
+ kMaxValue = kRecreateFramePoolFailed
+};
+
+void RecordStartCaptureResult(StartCaptureResult error) {
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.DesktopCapture.Win.WgcCaptureSessionStartResult",
+ static_cast<int>(error), static_cast<int>(StartCaptureResult::kMaxValue));
+}
+
+void RecordGetFrameResult(GetFrameResult error) {
+ RTC_HISTOGRAM_ENUMERATION(
+ "WebRTC.DesktopCapture.Win.WgcCaptureSessionGetFrameResult",
+ static_cast<int>(error), static_cast<int>(GetFrameResult::kMaxValue));
+}
+
+} // namespace
+
+WgcCaptureSession::WgcCaptureSession(ComPtr<ID3D11Device> d3d11_device,
+ ComPtr<WGC::IGraphicsCaptureItem> item,
+ ABI::Windows::Graphics::SizeInt32 size)
+ : d3d11_device_(std::move(d3d11_device)),
+ item_(std::move(item)),
+ size_(size) {}
+WgcCaptureSession::~WgcCaptureSession() {
+ RemoveEventHandlers();
+}
+
+HRESULT WgcCaptureSession::StartCapture() {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ RTC_DCHECK(!is_capture_started_);
+
+ if (item_closed_) {
+ RTC_LOG(LS_ERROR) << "The target source has been closed.";
+ RecordStartCaptureResult(StartCaptureResult::kSourceClosed);
+ return E_ABORT;
+ }
+
+ RTC_DCHECK(d3d11_device_);
+ RTC_DCHECK(item_);
+
+ // Listen for the Closed event, to detect if the source we are capturing is
+ // closed (e.g. application window is closed or monitor is disconnected). If
+ // it is, we should abort the capture.
+ item_closed_token_ = std::make_unique<EventRegistrationToken>();
+ auto closed_handler =
+ Microsoft::WRL::Callback<ABI::Windows::Foundation::ITypedEventHandler<
+ WGC::GraphicsCaptureItem*, IInspectable*>>(
+ this, &WgcCaptureSession::OnItemClosed);
+ HRESULT hr =
+ item_->add_Closed(closed_handler.Get(), item_closed_token_.get());
+ if (FAILED(hr)) {
+ RecordStartCaptureResult(StartCaptureResult::kAddClosedFailed);
+ return hr;
+ }
+
+ ComPtr<IDXGIDevice> dxgi_device;
+ hr = d3d11_device_->QueryInterface(IID_PPV_ARGS(&dxgi_device));
+ if (FAILED(hr)) {
+ RecordStartCaptureResult(StartCaptureResult::kDxgiDeviceCastFailed);
+ return hr;
+ }
+
+ if (!ResolveCoreWinRTDirect3DDelayload()) {
+ RecordStartCaptureResult(StartCaptureResult::kD3dDelayLoadFailed);
+ return E_FAIL;
+ }
+
+ hr = CreateDirect3DDeviceFromDXGIDevice(dxgi_device.Get(), &direct3d_device_);
+ if (FAILED(hr)) {
+ RecordStartCaptureResult(StartCaptureResult::kD3dDeviceCreationFailed);
+ return hr;
+ }
+
+ ComPtr<WGC::IDirect3D11CaptureFramePoolStatics> frame_pool_statics;
+ hr = GetActivationFactory<
+ WGC::IDirect3D11CaptureFramePoolStatics,
+ RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool>(
+ &frame_pool_statics);
+ if (FAILED(hr)) {
+ RecordStartCaptureResult(StartCaptureResult::kFramePoolActivationFailed);
+ return hr;
+ }
+
+ hr = frame_pool_statics->Create(direct3d_device_.Get(), kPixelFormat,
+ kNumBuffers, size_, &frame_pool_);
+ if (FAILED(hr)) {
+ RecordStartCaptureResult(StartCaptureResult::kCreateFramePoolFailed);
+ return hr;
+ }
+
+ frames_in_pool_ = 0;
+
+ // Because `WgcCapturerWin` created a `DispatcherQueue`, and we created
+ // `frame_pool_` via `Create`, the `FrameArrived` event will be delivered on
+ // the current thread.
+ frame_arrived_token_ = std::make_unique<EventRegistrationToken>();
+ auto frame_arrived_handler =
+ Microsoft::WRL::Callback<ABI::Windows::Foundation::ITypedEventHandler<
+ WGC::Direct3D11CaptureFramePool*, IInspectable*>>(
+ this, &WgcCaptureSession::OnFrameArrived);
+ hr = frame_pool_->add_FrameArrived(frame_arrived_handler.Get(),
+ frame_arrived_token_.get());
+
+ hr = frame_pool_->CreateCaptureSession(item_.Get(), &session_);
+ if (FAILED(hr)) {
+ RecordStartCaptureResult(StartCaptureResult::kCreateCaptureSessionFailed);
+ return hr;
+ }
+
+ hr = session_->StartCapture();
+ if (FAILED(hr)) {
+ RTC_LOG(LS_ERROR) << "Failed to start CaptureSession: " << hr;
+ RecordStartCaptureResult(StartCaptureResult::kStartCaptureFailed);
+ return hr;
+ }
+
+ RecordStartCaptureResult(StartCaptureResult::kSuccess);
+
+ is_capture_started_ = true;
+ return hr;
+}
+
+HRESULT WgcCaptureSession::GetFrame(
+ std::unique_ptr<DesktopFrame>* output_frame) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+
+ if (item_closed_) {
+ RTC_LOG(LS_ERROR) << "The target source has been closed.";
+ RecordGetFrameResult(GetFrameResult::kItemClosed);
+ return E_ABORT;
+ }
+
+ RTC_DCHECK(is_capture_started_);
+
+ if (frames_in_pool_ < 1)
+ wait_for_frame_event_.Wait(first_frame_ ? kMaxWaitForFirstFrameMs
+ : kMaxWaitForFrameMs);
+
+ ComPtr<WGC::IDirect3D11CaptureFrame> capture_frame;
+ HRESULT hr = frame_pool_->TryGetNextFrame(&capture_frame);
+ if (FAILED(hr)) {
+ RTC_LOG(LS_ERROR) << "TryGetNextFrame failed: " << hr;
+ RecordGetFrameResult(GetFrameResult::kTryGetNextFrameFailed);
+ return hr;
+ }
+
+ if (!capture_frame) {
+ RecordGetFrameResult(GetFrameResult::kFrameDropped);
+ return hr;
+ }
+
+ first_frame_ = false;
+ --frames_in_pool_;
+
+ // We need to get `capture_frame` as an `ID3D11Texture2D` so that we can get
+ // the raw image data in the format required by the `DesktopFrame` interface.
+ ComPtr<ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DSurface>
+ d3d_surface;
+ hr = capture_frame->get_Surface(&d3d_surface);
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kGetSurfaceFailed);
+ return hr;
+ }
+
+ ComPtr<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>
+ direct3DDxgiInterfaceAccess;
+ hr = d3d_surface->QueryInterface(IID_PPV_ARGS(&direct3DDxgiInterfaceAccess));
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kDxgiInterfaceAccessFailed);
+ return hr;
+ }
+
+ ComPtr<ID3D11Texture2D> texture_2D;
+ hr = direct3DDxgiInterfaceAccess->GetInterface(IID_PPV_ARGS(&texture_2D));
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kTexture2dCastFailed);
+ return hr;
+ }
+
+ if (!mapped_texture_) {
+ hr = CreateMappedTexture(texture_2D);
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kCreateMappedTextureFailed);
+ return hr;
+ }
+ }
+
+ // We need to copy `texture_2D` into `mapped_texture_` as the latter has the
+ // D3D11_CPU_ACCESS_READ flag set, which lets us access the image data.
+ // Otherwise it would only be readable by the GPU.
+ ComPtr<ID3D11DeviceContext> d3d_context;
+ d3d11_device_->GetImmediateContext(&d3d_context);
+
+ ABI::Windows::Graphics::SizeInt32 new_size;
+ hr = capture_frame->get_ContentSize(&new_size);
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kGetContentSizeFailed);
+ return hr;
+ }
+
+ // If the size changed, we must resize `mapped_texture_` and `frame_pool_` to
+ // fit the new size. This must be done before `CopySubresourceRegion` so that
+ // the textures are the same size.
+ if (size_.Height != new_size.Height || size_.Width != new_size.Width) {
+ hr = CreateMappedTexture(texture_2D, new_size.Width, new_size.Height);
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kResizeMappedTextureFailed);
+ return hr;
+ }
+
+ hr = frame_pool_->Recreate(direct3d_device_.Get(), kPixelFormat,
+ kNumBuffers, new_size);
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kRecreateFramePoolFailed);
+ return hr;
+ }
+ }
+
+ // If the size has changed since the last capture, we must be sure to use
+ // the smaller dimensions. Otherwise we might overrun our buffer, or
+ // read stale data from the last frame.
+ int image_height = std::min(size_.Height, new_size.Height);
+ int image_width = std::min(size_.Width, new_size.Width);
+
+ D3D11_BOX copy_region;
+ copy_region.left = 0;
+ copy_region.top = 0;
+ copy_region.right = image_width;
+ copy_region.bottom = image_height;
+ // Our textures are 2D so we just want one "slice" of the box.
+ copy_region.front = 0;
+ copy_region.back = 1;
+ d3d_context->CopySubresourceRegion(mapped_texture_.Get(),
+ /*dst_subresource_index=*/0, /*dst_x=*/0,
+ /*dst_y=*/0, /*dst_z=*/0, texture_2D.Get(),
+ /*src_subresource_index=*/0, &copy_region);
+
+ D3D11_MAPPED_SUBRESOURCE map_info;
+ hr = d3d_context->Map(mapped_texture_.Get(), /*subresource_index=*/0,
+ D3D11_MAP_READ, /*D3D11_MAP_FLAG_DO_NOT_WAIT=*/0,
+ &map_info);
+ if (FAILED(hr)) {
+ RecordGetFrameResult(GetFrameResult::kMapFrameFailed);
+ return hr;
+ }
+
+ int row_data_length = image_width * DesktopFrame::kBytesPerPixel;
+
+ // Make a copy of the data pointed to by `map_info.pData` so we are free to
+ // unmap our texture.
+ uint8_t* src_data = static_cast<uint8_t*>(map_info.pData);
+ std::vector<uint8_t> image_data;
+ image_data.resize(image_height * row_data_length);
+ uint8_t* image_data_ptr = image_data.data();
+ for (int i = 0; i < image_height; i++) {
+ memcpy(image_data_ptr, src_data, row_data_length);
+ image_data_ptr += row_data_length;
+ src_data += map_info.RowPitch;
+ }
+
+ d3d_context->Unmap(mapped_texture_.Get(), 0);
+
+ // Transfer ownership of `image_data` to the output_frame.
+ DesktopSize size(image_width, image_height);
+ *output_frame = std::make_unique<WgcDesktopFrame>(size, row_data_length,
+ std::move(image_data));
+
+ size_ = new_size;
+ RecordGetFrameResult(GetFrameResult::kSuccess);
+ return hr;
+}
+
+HRESULT WgcCaptureSession::CreateMappedTexture(
+ ComPtr<ID3D11Texture2D> src_texture,
+ UINT width,
+ UINT height) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+
+ D3D11_TEXTURE2D_DESC src_desc;
+ src_texture->GetDesc(&src_desc);
+ D3D11_TEXTURE2D_DESC map_desc;
+ map_desc.Width = width == 0 ? src_desc.Width : width;
+ map_desc.Height = height == 0 ? src_desc.Height : height;
+ map_desc.MipLevels = src_desc.MipLevels;
+ map_desc.ArraySize = src_desc.ArraySize;
+ map_desc.Format = src_desc.Format;
+ map_desc.SampleDesc = src_desc.SampleDesc;
+ map_desc.Usage = D3D11_USAGE_STAGING;
+ map_desc.BindFlags = 0;
+ map_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ map_desc.MiscFlags = 0;
+ return d3d11_device_->CreateTexture2D(&map_desc, nullptr, &mapped_texture_);
+}
+
+HRESULT WgcCaptureSession::OnFrameArrived(
+ WGC::IDirect3D11CaptureFramePool* sender,
+ IInspectable* event_args) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ RTC_DCHECK_LT(frames_in_pool_, kNumBuffers);
+ ++frames_in_pool_;
+ wait_for_frame_event_.Set();
+ return S_OK;
+}
+
+HRESULT WgcCaptureSession::OnItemClosed(WGC::IGraphicsCaptureItem* sender,
+ IInspectable* event_args) {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+
+ RTC_LOG(LS_INFO) << "Capture target has been closed.";
+ item_closed_ = true;
+ is_capture_started_ = false;
+
+ RemoveEventHandlers();
+
+ mapped_texture_ = nullptr;
+ session_ = nullptr;
+ frame_pool_ = nullptr;
+ direct3d_device_ = nullptr;
+ item_ = nullptr;
+ d3d11_device_ = nullptr;
+
+ return S_OK;
+}
+
+void WgcCaptureSession::RemoveEventHandlers() {
+ HRESULT hr;
+ if (frame_pool_ && frame_arrived_token_) {
+ hr = frame_pool_->remove_FrameArrived(*frame_arrived_token_);
+ frame_arrived_token_.reset();
+ if (FAILED(hr)) {
+ RTC_LOG(LS_WARNING) << "Failed to remove FrameArrived event handler: "
+ << hr;
+ }
+ }
+ if (item_ && item_closed_token_) {
+ hr = item_->remove_Closed(*item_closed_token_);
+ item_closed_token_.reset();
+ if (FAILED(hr))
+ RTC_LOG(LS_WARNING) << "Failed to remove Closed event handler: " << hr;
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.h b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.h
new file mode 100644
index 0000000000..27d412baf9
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_session.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_
+
+#include <d3d11.h>
+#include <windows.graphics.capture.h>
+#include <windows.graphics.h>
+#include <wrl/client.h>
+
+#include <memory>
+
+#include "api/sequence_checker.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/win/wgc_capture_source.h"
+#include "rtc_base/event.h"
+
+namespace webrtc {
+
+class WgcCaptureSession final {
+ public:
+ WgcCaptureSession(
+ Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device,
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Capture::IGraphicsCaptureItem> item,
+ ABI::Windows::Graphics::SizeInt32 size);
+
+ // Disallow copy and assign.
+ WgcCaptureSession(const WgcCaptureSession&) = delete;
+ WgcCaptureSession& operator=(const WgcCaptureSession&) = delete;
+
+ ~WgcCaptureSession();
+
+ HRESULT StartCapture();
+
+ // Returns a frame from the frame pool, if any are present.
+ HRESULT GetFrame(std::unique_ptr<DesktopFrame>* output_frame);
+
+ bool IsCaptureStarted() const {
+ RTC_DCHECK_RUN_ON(&sequence_checker_);
+ return is_capture_started_;
+ }
+
+ // We keep 2 buffers in the frame pool to balance the staleness of the frame
+ // with having to wait for frames to arrive too frequently. Too many buffers
+ // will lead to a high latency, and too few will lead to poor performance.
+ // We make this public for tests.
+ static constexpr int kNumBuffers = 2;
+
+ private:
+ // Initializes `mapped_texture_` with the properties of the `src_texture`,
+ // overrides the values of some necessary properties like the
+ // D3D11_CPU_ACCESS_READ flag. Also has optional parameters for what size
+ // `mapped_texture_` should be, if they aren't provided we will use the size
+ // of `src_texture`.
+ HRESULT CreateMappedTexture(
+ Microsoft::WRL::ComPtr<ID3D11Texture2D> src_texture,
+ UINT width = 0,
+ UINT height = 0);
+
+ // Event handler for `item_`'s Closed event.
+ HRESULT OnItemClosed(
+ ABI::Windows::Graphics::Capture::IGraphicsCaptureItem* sender,
+ IInspectable* event_args);
+
+ // Event handler for `frame_pool_`'s FrameArrived event.
+ HRESULT OnFrameArrived(
+ ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool* sender,
+ IInspectable* event_args);
+
+ void RemoveEventHandlers();
+
+ // We wait on this event in `GetFrame` if there are no frames in the pool.
+ // `OnFrameArrived` will set the event so we can proceed.
+ rtc::Event wait_for_frame_event_;
+ int frames_in_pool_;
+
+ // We're willing to wait for a frame a little longer if it's the first one.
+ bool first_frame_ = true;
+
+ std::unique_ptr<EventRegistrationToken> frame_arrived_token_;
+ std::unique_ptr<EventRegistrationToken> item_closed_token_;
+
+ // A Direct3D11 Device provided by the caller. We use this to create an
+ // IDirect3DDevice, and also to create textures that will hold the image data.
+ Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device_;
+
+ // This item represents what we are capturing, we use it to create the
+ // capture session, and also to listen for the Closed event.
+ Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>
+ item_;
+
+ // The IDirect3DDevice is necessary to instantiate the frame pool.
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DDevice>
+ direct3d_device_;
+
+ // The frame pool is where frames are deposited during capture, we retrieve
+ // them from here with TryGetNextFrame().
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePool>
+ frame_pool_;
+
+ // This texture holds the final image data. We made it a member so we can
+ // reuse it, instead of having to create a new texture every time we grab a
+ // frame.
+ Microsoft::WRL::ComPtr<ID3D11Texture2D> mapped_texture_;
+
+ // This is the size of `mapped_texture_` and the buffers in `frame_pool_`. We
+ // store this as a member so we can compare it to the size of incoming frames
+ // and resize if necessary.
+ ABI::Windows::Graphics::SizeInt32 size_;
+
+ // The capture session lets us set properties about the capture before it
+ // starts such as whether to capture the mouse cursor, and it lets us tell WGC
+ // to start capturing frames.
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Capture::IGraphicsCaptureSession>
+ session_;
+
+ bool item_closed_ = false;
+ bool is_capture_started_ = false;
+
+ SequenceChecker sequence_checker_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SESSION_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.cc b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.cc
new file mode 100644
index 0000000000..24e6129ec7
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.cc
@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/wgc_capture_source.h"
+
+#include <dwmapi.h>
+#include <windows.graphics.capture.interop.h>
+#include <windows.h>
+
+#include <utility>
+
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "modules/desktop_capture/win/window_capture_utils.h"
+#include "rtc_base/win/get_activation_factory.h"
+
+using Microsoft::WRL::ComPtr;
+namespace WGC = ABI::Windows::Graphics::Capture;
+
+namespace webrtc {
+
+WgcCaptureSource::WgcCaptureSource(DesktopCapturer::SourceId source_id)
+ : source_id_(source_id) {}
+WgcCaptureSource::~WgcCaptureSource() = default;
+
+bool WgcCaptureSource::IsCapturable() {
+ // If we can create a capture item, then we can capture it. Unfortunately,
+ // we can't cache this item because it may be created in a different COM
+ // apartment than where capture will eventually start from.
+ ComPtr<WGC::IGraphicsCaptureItem> item;
+ return SUCCEEDED(CreateCaptureItem(&item));
+}
+
+bool WgcCaptureSource::FocusOnSource() {
+ return false;
+}
+
+ABI::Windows::Graphics::SizeInt32 WgcCaptureSource::GetSize() {
+ if (!item_)
+ return {0, 0};
+
+ ABI::Windows::Graphics::SizeInt32 item_size;
+ HRESULT hr = item_->get_Size(&item_size);
+ if (FAILED(hr))
+ return {0, 0};
+
+ return item_size;
+}
+
+HRESULT WgcCaptureSource::GetCaptureItem(
+ ComPtr<WGC::IGraphicsCaptureItem>* result) {
+ HRESULT hr = S_OK;
+ if (!item_)
+ hr = CreateCaptureItem(&item_);
+
+ *result = item_;
+ return hr;
+}
+
+WgcCaptureSourceFactory::~WgcCaptureSourceFactory() = default;
+
+WgcWindowSourceFactory::WgcWindowSourceFactory() = default;
+WgcWindowSourceFactory::~WgcWindowSourceFactory() = default;
+
+std::unique_ptr<WgcCaptureSource> WgcWindowSourceFactory::CreateCaptureSource(
+ DesktopCapturer::SourceId source_id) {
+ return std::make_unique<WgcWindowSource>(source_id);
+}
+
+WgcScreenSourceFactory::WgcScreenSourceFactory() = default;
+WgcScreenSourceFactory::~WgcScreenSourceFactory() = default;
+
+std::unique_ptr<WgcCaptureSource> WgcScreenSourceFactory::CreateCaptureSource(
+ DesktopCapturer::SourceId source_id) {
+ return std::make_unique<WgcScreenSource>(source_id);
+}
+
+WgcWindowSource::WgcWindowSource(DesktopCapturer::SourceId source_id)
+ : WgcCaptureSource(source_id) {}
+WgcWindowSource::~WgcWindowSource() = default;
+
+DesktopVector WgcWindowSource::GetTopLeft() {
+ DesktopRect window_rect;
+ if (!GetWindowRect(reinterpret_cast<HWND>(GetSourceId()), &window_rect))
+ return DesktopVector();
+
+ return window_rect.top_left();
+}
+
+ABI::Windows::Graphics::SizeInt32 WgcWindowSource::GetSize() {
+ RECT window_rect;
+ HRESULT hr = ::DwmGetWindowAttribute(
+ reinterpret_cast<HWND>(GetSourceId()), DWMWA_EXTENDED_FRAME_BOUNDS,
+ reinterpret_cast<void*>(&window_rect), sizeof(window_rect));
+ if (FAILED(hr))
+ return WgcCaptureSource::GetSize();
+
+ return {window_rect.right - window_rect.left,
+ window_rect.bottom - window_rect.top};
+}
+
+bool WgcWindowSource::IsCapturable() {
+ if (!IsWindowValidAndVisible(reinterpret_cast<HWND>(GetSourceId())))
+ return false;
+
+ return WgcCaptureSource::IsCapturable();
+}
+
+bool WgcWindowSource::FocusOnSource() {
+ if (!IsWindowValidAndVisible(reinterpret_cast<HWND>(GetSourceId())))
+ return false;
+
+ return ::BringWindowToTop(reinterpret_cast<HWND>(GetSourceId())) &&
+ ::SetForegroundWindow(reinterpret_cast<HWND>(GetSourceId()));
+}
+
+HRESULT WgcWindowSource::CreateCaptureItem(
+ ComPtr<WGC::IGraphicsCaptureItem>* result) {
+ if (!ResolveCoreWinRTDelayload())
+ return E_FAIL;
+
+ ComPtr<IGraphicsCaptureItemInterop> interop;
+ HRESULT hr = GetActivationFactory<
+ IGraphicsCaptureItemInterop,
+ RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem>(&interop);
+ if (FAILED(hr))
+ return hr;
+
+ ComPtr<WGC::IGraphicsCaptureItem> item;
+ hr = interop->CreateForWindow(reinterpret_cast<HWND>(GetSourceId()),
+ IID_PPV_ARGS(&item));
+ if (FAILED(hr))
+ return hr;
+
+ if (!item)
+ return E_HANDLE;
+
+ *result = std::move(item);
+ return hr;
+}
+
+WgcScreenSource::WgcScreenSource(DesktopCapturer::SourceId source_id)
+ : WgcCaptureSource(source_id) {
+ // Getting the HMONITOR could fail if the source_id is invalid. In that case,
+ // we leave hmonitor_ uninitialized and `IsCapturable()` will fail.
+ HMONITOR hmon;
+ if (GetHmonitorFromDeviceIndex(GetSourceId(), &hmon))
+ hmonitor_ = hmon;
+}
+
+WgcScreenSource::~WgcScreenSource() = default;
+
+DesktopVector WgcScreenSource::GetTopLeft() {
+ if (!hmonitor_)
+ return DesktopVector();
+
+ return GetMonitorRect(*hmonitor_).top_left();
+}
+
+ABI::Windows::Graphics::SizeInt32 WgcScreenSource::GetSize() {
+ ABI::Windows::Graphics::SizeInt32 size = WgcCaptureSource::GetSize();
+ if (!hmonitor_ || (size.Width != 0 && size.Height != 0))
+ return size;
+
+ DesktopRect rect = GetMonitorRect(*hmonitor_);
+ return {rect.width(), rect.height()};
+}
+
+bool WgcScreenSource::IsCapturable() {
+ if (!hmonitor_)
+ return false;
+
+ if (!IsMonitorValid(*hmonitor_))
+ return false;
+
+ return WgcCaptureSource::IsCapturable();
+}
+
+HRESULT WgcScreenSource::CreateCaptureItem(
+ ComPtr<WGC::IGraphicsCaptureItem>* result) {
+ if (!hmonitor_)
+ return E_ABORT;
+
+ if (!ResolveCoreWinRTDelayload())
+ return E_FAIL;
+
+ ComPtr<IGraphicsCaptureItemInterop> interop;
+ HRESULT hr = GetActivationFactory<
+ IGraphicsCaptureItemInterop,
+ RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureItem>(&interop);
+ if (FAILED(hr))
+ return hr;
+
+ // Ensure the monitor is still valid (hasn't disconnected) before trying to
+ // create the item. On versions of Windows before Win11, `CreateForMonitor`
+ // will crash if no displays are connected.
+ if (!IsMonitorValid(hmonitor_.value()))
+ return E_ABORT;
+
+ ComPtr<WGC::IGraphicsCaptureItem> item;
+ hr = interop->CreateForMonitor(*hmonitor_, IID_PPV_ARGS(&item));
+ if (FAILED(hr))
+ return hr;
+
+ if (!item)
+ return E_HANDLE;
+
+ *result = std::move(item);
+ return hr;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.h b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.h
new file mode 100644
index 0000000000..d1275b6168
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SOURCE_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SOURCE_H_
+
+#include <windows.graphics.capture.h>
+#include <windows.graphics.h>
+#include <wrl/client.h>
+
+#include <memory>
+
+#include "absl/types/optional.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+// Abstract class to represent the source that WGC-based capturers capture
+// from. Could represent an application window or a screen. Consumers should use
+// the appropriate Wgc*SourceFactory class to create WgcCaptureSource objects
+// of the appropriate type.
+class WgcCaptureSource {
+ public:
+ explicit WgcCaptureSource(DesktopCapturer::SourceId source_id);
+ virtual ~WgcCaptureSource();
+
+ virtual DesktopVector GetTopLeft() = 0;
+ virtual bool IsCapturable();
+ virtual bool FocusOnSource();
+ virtual ABI::Windows::Graphics::SizeInt32 GetSize();
+ HRESULT GetCaptureItem(
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result);
+ DesktopCapturer::SourceId GetSourceId() { return source_id_; }
+
+ protected:
+ virtual HRESULT CreateCaptureItem(
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result) = 0;
+
+ private:
+ Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>
+ item_;
+ const DesktopCapturer::SourceId source_id_;
+};
+
+class WgcCaptureSourceFactory {
+ public:
+ virtual ~WgcCaptureSourceFactory();
+
+ virtual std::unique_ptr<WgcCaptureSource> CreateCaptureSource(
+ DesktopCapturer::SourceId) = 0;
+};
+
+class WgcWindowSourceFactory final : public WgcCaptureSourceFactory {
+ public:
+ WgcWindowSourceFactory();
+
+ // Disallow copy and assign.
+ WgcWindowSourceFactory(const WgcWindowSourceFactory&) = delete;
+ WgcWindowSourceFactory& operator=(const WgcWindowSourceFactory&) = delete;
+
+ ~WgcWindowSourceFactory() override;
+
+ std::unique_ptr<WgcCaptureSource> CreateCaptureSource(
+ DesktopCapturer::SourceId) override;
+};
+
+class WgcScreenSourceFactory final : public WgcCaptureSourceFactory {
+ public:
+ WgcScreenSourceFactory();
+
+ WgcScreenSourceFactory(const WgcScreenSourceFactory&) = delete;
+ WgcScreenSourceFactory& operator=(const WgcScreenSourceFactory&) = delete;
+
+ ~WgcScreenSourceFactory() override;
+
+ std::unique_ptr<WgcCaptureSource> CreateCaptureSource(
+ DesktopCapturer::SourceId) override;
+};
+
+// Class for capturing application windows.
+class WgcWindowSource final : public WgcCaptureSource {
+ public:
+ explicit WgcWindowSource(DesktopCapturer::SourceId source_id);
+
+ WgcWindowSource(const WgcWindowSource&) = delete;
+ WgcWindowSource& operator=(const WgcWindowSource&) = delete;
+
+ ~WgcWindowSource() override;
+
+ DesktopVector GetTopLeft() override;
+ ABI::Windows::Graphics::SizeInt32 GetSize() override;
+ bool IsCapturable() override;
+ bool FocusOnSource() override;
+
+ private:
+ HRESULT CreateCaptureItem(
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result)
+ override;
+};
+
+// Class for capturing screens/monitors/displays.
+class WgcScreenSource final : public WgcCaptureSource {
+ public:
+ explicit WgcScreenSource(DesktopCapturer::SourceId source_id);
+
+ WgcScreenSource(const WgcScreenSource&) = delete;
+ WgcScreenSource& operator=(const WgcScreenSource&) = delete;
+
+ ~WgcScreenSource() override;
+
+ DesktopVector GetTopLeft() override;
+ ABI::Windows::Graphics::SizeInt32 GetSize() override;
+ bool IsCapturable() override;
+
+ private:
+ HRESULT CreateCaptureItem(
+ Microsoft::WRL::ComPtr<
+ ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>* result)
+ override;
+
+ // To maintain compatibility with other capturers, this class accepts a
+ // device index as it's SourceId. However, WGC requires we use an HMONITOR to
+ // describe which screen to capture. So, we internally convert the supplied
+ // device index into an HMONITOR when `IsCapturable()` is called.
+ absl::optional<HMONITOR> hmonitor_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURE_SOURCE_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source_unittest.cc
new file mode 100644
index 0000000000..dc37ec2e0d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capture_source_unittest.cc
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/wgc_capture_source.h"
+
+#include <windows.graphics.capture.h>
+#include <wrl/client.h>
+
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "modules/desktop_capture/win/test_support/test_window.h"
+#include "modules/desktop_capture/win/wgc_capturer_win.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/win/scoped_com_initializer.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+const WCHAR kWindowTitle[] = L"WGC Capture Source Test Window";
+
+const int kFirstXCoord = 25;
+const int kFirstYCoord = 50;
+const int kSecondXCoord = 50;
+const int kSecondYCoord = 75;
+
+} // namespace
+
+class WgcCaptureSourceTest : public ::testing::TestWithParam<CaptureType> {
+ public:
+ void SetUp() override {
+ com_initializer_ =
+ std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
+ ASSERT_TRUE(com_initializer_->Succeeded());
+ }
+
+ void TearDown() override {
+ if (window_open_) {
+ DestroyTestWindow(window_info_);
+ }
+ }
+
+ void SetUpForWindowSource() {
+ window_info_ = CreateTestWindow(kWindowTitle);
+ window_open_ = true;
+ source_id_ = reinterpret_cast<DesktopCapturer::SourceId>(window_info_.hwnd);
+ source_factory_ = std::make_unique<WgcWindowSourceFactory>();
+ }
+
+ void SetUpForScreenSource() {
+ source_id_ = kFullDesktopScreenId;
+ source_factory_ = std::make_unique<WgcScreenSourceFactory>();
+ }
+
+ protected:
+ std::unique_ptr<ScopedCOMInitializer> com_initializer_;
+ std::unique_ptr<WgcCaptureSourceFactory> source_factory_;
+ std::unique_ptr<WgcCaptureSource> source_;
+ DesktopCapturer::SourceId source_id_;
+ WindowInfo window_info_;
+ bool window_open_ = false;
+};
+
+// Window specific test
+TEST_F(WgcCaptureSourceTest, WindowPosition) {
+ if (!IsWgcSupported(CaptureType::kWindow)) {
+ RTC_LOG(LS_INFO)
+ << "Skipping WgcCapturerWinTests on unsupported platforms.";
+ GTEST_SKIP();
+ }
+
+ SetUpForWindowSource();
+ source_ = source_factory_->CreateCaptureSource(source_id_);
+ ASSERT_TRUE(source_);
+ EXPECT_EQ(source_->GetSourceId(), source_id_);
+
+ MoveTestWindow(window_info_.hwnd, kFirstXCoord, kFirstYCoord);
+ DesktopVector source_vector = source_->GetTopLeft();
+ EXPECT_EQ(source_vector.x(), kFirstXCoord);
+ EXPECT_EQ(source_vector.y(), kFirstYCoord);
+
+ MoveTestWindow(window_info_.hwnd, kSecondXCoord, kSecondYCoord);
+ source_vector = source_->GetTopLeft();
+ EXPECT_EQ(source_vector.x(), kSecondXCoord);
+ EXPECT_EQ(source_vector.y(), kSecondYCoord);
+}
+
+// Screen specific test
+TEST_F(WgcCaptureSourceTest, ScreenPosition) {
+ if (!IsWgcSupported(CaptureType::kScreen)) {
+ RTC_LOG(LS_INFO)
+ << "Skipping WgcCapturerWinTests on unsupported platforms.";
+ GTEST_SKIP();
+ }
+
+ SetUpForScreenSource();
+ source_ = source_factory_->CreateCaptureSource(source_id_);
+ ASSERT_TRUE(source_);
+ EXPECT_EQ(source_id_, source_->GetSourceId());
+
+ DesktopRect screen_rect = GetFullscreenRect();
+ DesktopVector source_vector = source_->GetTopLeft();
+ EXPECT_EQ(source_vector.x(), screen_rect.left());
+ EXPECT_EQ(source_vector.y(), screen_rect.top());
+}
+
+// Source agnostic test
+TEST_P(WgcCaptureSourceTest, CreateSource) {
+ if (!IsWgcSupported(GetParam())) {
+ RTC_LOG(LS_INFO)
+ << "Skipping WgcCapturerWinTests on unsupported platforms.";
+ GTEST_SKIP();
+ }
+
+ if (GetParam() == CaptureType::kWindow) {
+ SetUpForWindowSource();
+ } else {
+ SetUpForScreenSource();
+ }
+
+ source_ = source_factory_->CreateCaptureSource(source_id_);
+ ASSERT_TRUE(source_);
+ EXPECT_EQ(source_id_, source_->GetSourceId());
+ EXPECT_TRUE(source_->IsCapturable());
+
+ Microsoft::WRL::ComPtr<ABI::Windows::Graphics::Capture::IGraphicsCaptureItem>
+ item;
+ EXPECT_TRUE(SUCCEEDED(source_->GetCaptureItem(&item)));
+ EXPECT_TRUE(item);
+}
+
+INSTANTIATE_TEST_SUITE_P(SourceAgnostic,
+ WgcCaptureSourceTest,
+ ::testing::Values(CaptureType::kWindow,
+ CaptureType::kScreen));
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.cc b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.cc
new file mode 100644
index 0000000000..ce5eb6b31f
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.cc
@@ -0,0 +1,363 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/wgc_capturer_win.h"
+
+#include <DispatcherQueue.h>
+#include <windows.foundation.metadata.h>
+#include <windows.graphics.capture.h>
+
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capture_metrics_helper.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/win/wgc_desktop_frame.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/win/get_activation_factory.h"
+#include "rtc_base/win/hstring.h"
+#include "rtc_base/win/windows_version.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace WGC = ABI::Windows::Graphics::Capture;
+using Microsoft::WRL::ComPtr;
+
+namespace webrtc {
+
+namespace {
+
+constexpr wchar_t kCoreMessagingDll[] = L"CoreMessaging.dll";
+
+constexpr wchar_t kWgcSessionType[] =
+ L"Windows.Graphics.Capture.GraphicsCaptureSession";
+constexpr wchar_t kApiContract[] = L"Windows.Foundation.UniversalApiContract";
+constexpr UINT16 kRequiredApiContractVersion = 8;
+
+enum class WgcCapturerResult {
+ kSuccess = 0,
+ kNoDirect3dDevice = 1,
+ kNoSourceSelected = 2,
+ kItemCreationFailure = 3,
+ kSessionStartFailure = 4,
+ kGetFrameFailure = 5,
+ kFrameDropped = 6,
+ kCreateDispatcherQueueFailure = 7,
+ kMaxValue = kCreateDispatcherQueueFailure
+};
+
+void RecordWgcCapturerResult(WgcCapturerResult error) {
+ RTC_HISTOGRAM_ENUMERATION("WebRTC.DesktopCapture.Win.WgcCapturerResult",
+ static_cast<int>(error),
+ static_cast<int>(WgcCapturerResult::kMaxValue));
+}
+
+} // namespace
+
+bool IsWgcSupported(CaptureType capture_type) {
+ if (!HasActiveDisplay()) {
+ // There is a bug in `CreateForMonitor` that causes a crash if there are no
+ // active displays. The crash was fixed in Win11, but we are still unable
+ // to capture screens without an active display.
+ if (capture_type == CaptureType::kScreen)
+ return false;
+
+ // There is a bug in the DWM (Desktop Window Manager) that prevents it from
+ // providing image data if there are no displays attached. This was fixed in
+ // Windows 11.
+ if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN11)
+ return false;
+ }
+
+ // A bug in the WGC API `CreateForMonitor` prevents capturing the entire
+ // virtual screen (all monitors simultaneously), this was fixed in 20H1. Since
+ // we can't assert that we won't be asked to capture the entire virtual
+ // screen, we report unsupported so we can fallback to another capturer.
+ if (capture_type == CaptureType::kScreen &&
+ rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN10_20H1) {
+ return false;
+ }
+
+ if (!ResolveCoreWinRTDelayload())
+ return false;
+
+ // We need to check if the WGC APIs are presesnt on the system. Certain SKUs
+ // of Windows ship without these APIs.
+ ComPtr<ABI::Windows::Foundation::Metadata::IApiInformationStatics>
+ api_info_statics;
+ HRESULT hr = GetActivationFactory<
+ ABI::Windows::Foundation::Metadata::IApiInformationStatics,
+ RuntimeClass_Windows_Foundation_Metadata_ApiInformation>(
+ &api_info_statics);
+ if (FAILED(hr))
+ return false;
+
+ HSTRING api_contract;
+ hr = webrtc::CreateHstring(kApiContract, wcslen(kApiContract), &api_contract);
+ if (FAILED(hr))
+ return false;
+
+ boolean is_api_present;
+ hr = api_info_statics->IsApiContractPresentByMajor(
+ api_contract, kRequiredApiContractVersion, &is_api_present);
+ webrtc::DeleteHstring(api_contract);
+ if (FAILED(hr) || !is_api_present)
+ return false;
+
+ HSTRING wgc_session_type;
+ hr = webrtc::CreateHstring(kWgcSessionType, wcslen(kWgcSessionType),
+ &wgc_session_type);
+ if (FAILED(hr))
+ return false;
+
+ boolean is_type_present;
+ hr = api_info_statics->IsTypePresent(wgc_session_type, &is_type_present);
+ webrtc::DeleteHstring(wgc_session_type);
+ if (FAILED(hr) || !is_type_present)
+ return false;
+
+ // If the APIs are present, we need to check that they are supported.
+ ComPtr<WGC::IGraphicsCaptureSessionStatics> capture_session_statics;
+ hr = GetActivationFactory<
+ WGC::IGraphicsCaptureSessionStatics,
+ RuntimeClass_Windows_Graphics_Capture_GraphicsCaptureSession>(
+ &capture_session_statics);
+ if (FAILED(hr))
+ return false;
+
+ boolean is_supported;
+ hr = capture_session_statics->IsSupported(&is_supported);
+ if (FAILED(hr) || !is_supported)
+ return false;
+
+ return true;
+}
+
+WgcCapturerWin::WgcCapturerWin(
+ std::unique_ptr<WgcCaptureSourceFactory> source_factory,
+ std::unique_ptr<SourceEnumerator> source_enumerator,
+ bool allow_delayed_capturable_check)
+ : source_factory_(std::move(source_factory)),
+ source_enumerator_(std::move(source_enumerator)),
+ allow_delayed_capturable_check_(allow_delayed_capturable_check) {
+ if (!core_messaging_library_)
+ core_messaging_library_ = LoadLibraryW(kCoreMessagingDll);
+
+ if (core_messaging_library_) {
+ create_dispatcher_queue_controller_func_ =
+ reinterpret_cast<CreateDispatcherQueueControllerFunc>(GetProcAddress(
+ core_messaging_library_, "CreateDispatcherQueueController"));
+ }
+}
+
+WgcCapturerWin::~WgcCapturerWin() {
+ if (core_messaging_library_)
+ FreeLibrary(core_messaging_library_);
+}
+
+// static
+std::unique_ptr<DesktopCapturer> WgcCapturerWin::CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options,
+ bool allow_delayed_capturable_check) {
+ return std::make_unique<WgcCapturerWin>(
+ std::make_unique<WgcWindowSourceFactory>(),
+ std::make_unique<WindowEnumerator>(
+ options.enumerate_current_process_windows()),
+ allow_delayed_capturable_check);
+}
+
+// static
+std::unique_ptr<DesktopCapturer> WgcCapturerWin::CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options) {
+ return std::make_unique<WgcCapturerWin>(
+ std::make_unique<WgcScreenSourceFactory>(),
+ std::make_unique<ScreenEnumerator>(), false);
+}
+
+bool WgcCapturerWin::GetSourceList(SourceList* sources) {
+ return source_enumerator_->FindAllSources(sources);
+}
+
+bool WgcCapturerWin::SelectSource(DesktopCapturer::SourceId id) {
+ capture_source_ = source_factory_->CreateCaptureSource(id);
+ if (allow_delayed_capturable_check_)
+ return true;
+
+ return capture_source_->IsCapturable();
+}
+
+bool WgcCapturerWin::FocusOnSelectedSource() {
+ if (!capture_source_)
+ return false;
+
+ return capture_source_->FocusOnSource();
+}
+
+void WgcCapturerWin::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+ RecordCapturerImpl(DesktopCapturerId::kWgcCapturerWin);
+
+ callback_ = callback;
+
+ // Create a Direct3D11 device to share amongst the WgcCaptureSessions. Many
+ // parameters are nullptr as the implemention uses defaults that work well for
+ // us.
+ HRESULT hr = D3D11CreateDevice(
+ /*adapter=*/nullptr, D3D_DRIVER_TYPE_HARDWARE,
+ /*software_rasterizer=*/nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
+ /*feature_levels=*/nullptr, /*feature_levels_size=*/0, D3D11_SDK_VERSION,
+ &d3d11_device_, /*feature_level=*/nullptr, /*device_context=*/nullptr);
+ if (hr == DXGI_ERROR_UNSUPPORTED) {
+ // If a hardware device could not be created, use WARP which is a high speed
+ // software device.
+ hr = D3D11CreateDevice(
+ /*adapter=*/nullptr, D3D_DRIVER_TYPE_WARP,
+ /*software_rasterizer=*/nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT,
+ /*feature_levels=*/nullptr, /*feature_levels_size=*/0,
+ D3D11_SDK_VERSION, &d3d11_device_, /*feature_level=*/nullptr,
+ /*device_context=*/nullptr);
+ }
+
+ if (FAILED(hr)) {
+ RTC_LOG(LS_ERROR) << "Failed to create D3D11Device: " << hr;
+ }
+}
+
+void WgcCapturerWin::CaptureFrame() {
+ RTC_DCHECK(callback_);
+
+ if (!capture_source_) {
+ RTC_LOG(LS_ERROR) << "Source hasn't been selected";
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
+ /*frame=*/nullptr);
+ RecordWgcCapturerResult(WgcCapturerResult::kNoSourceSelected);
+ return;
+ }
+
+ if (!d3d11_device_) {
+ RTC_LOG(LS_ERROR) << "No D3D11D3evice, cannot capture.";
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
+ /*frame=*/nullptr);
+ RecordWgcCapturerResult(WgcCapturerResult::kNoDirect3dDevice);
+ return;
+ }
+
+ if (allow_delayed_capturable_check_ && !capture_source_->IsCapturable()) {
+ RTC_LOG(LS_ERROR) << "Source is not capturable.";
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
+ /*frame=*/nullptr);
+ return;
+ }
+
+ HRESULT hr;
+ if (!dispatcher_queue_created_) {
+ // Set the apartment type to NONE because this thread should already be COM
+ // initialized.
+ DispatcherQueueOptions options{
+ sizeof(DispatcherQueueOptions),
+ DISPATCHERQUEUE_THREAD_TYPE::DQTYPE_THREAD_CURRENT,
+ DISPATCHERQUEUE_THREAD_APARTMENTTYPE::DQTAT_COM_NONE};
+ ComPtr<ABI::Windows::System::IDispatcherQueueController> queue_controller;
+ hr = create_dispatcher_queue_controller_func_(options, &queue_controller);
+
+ // If there is already a DispatcherQueue on this thread, that is fine. Its
+ // lifetime is tied to the thread's, and as long as the thread has one, even
+ // if we didn't create it, the capture session's events will be delivered on
+ // this thread.
+ if (FAILED(hr) && hr != RPC_E_WRONG_THREAD) {
+ RecordWgcCapturerResult(WgcCapturerResult::kCreateDispatcherQueueFailure);
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
+ /*frame=*/nullptr);
+ } else {
+ dispatcher_queue_created_ = true;
+ }
+ }
+
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ WgcCaptureSession* capture_session = nullptr;
+ std::map<SourceId, WgcCaptureSession>::iterator session_iter =
+ ongoing_captures_.find(capture_source_->GetSourceId());
+ if (session_iter == ongoing_captures_.end()) {
+ ComPtr<WGC::IGraphicsCaptureItem> item;
+ hr = capture_source_->GetCaptureItem(&item);
+ if (FAILED(hr)) {
+ RTC_LOG(LS_ERROR) << "Failed to create a GraphicsCaptureItem: " << hr;
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
+ /*frame=*/nullptr);
+ RecordWgcCapturerResult(WgcCapturerResult::kItemCreationFailure);
+ return;
+ }
+
+ std::pair<std::map<SourceId, WgcCaptureSession>::iterator, bool>
+ iter_success_pair = ongoing_captures_.emplace(
+ std::piecewise_construct,
+ std::forward_as_tuple(capture_source_->GetSourceId()),
+ std::forward_as_tuple(d3d11_device_, item,
+ capture_source_->GetSize()));
+ RTC_DCHECK(iter_success_pair.second);
+ capture_session = &iter_success_pair.first->second;
+ } else {
+ capture_session = &session_iter->second;
+ }
+
+ if (!capture_session->IsCaptureStarted()) {
+ hr = capture_session->StartCapture();
+ if (FAILED(hr)) {
+ RTC_LOG(LS_ERROR) << "Failed to start capture: " << hr;
+ ongoing_captures_.erase(capture_source_->GetSourceId());
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
+ /*frame=*/nullptr);
+ RecordWgcCapturerResult(WgcCapturerResult::kSessionStartFailure);
+ return;
+ }
+ }
+
+ std::unique_ptr<DesktopFrame> frame;
+ hr = capture_session->GetFrame(&frame);
+ if (FAILED(hr)) {
+ RTC_LOG(LS_ERROR) << "GetFrame failed: " << hr;
+ ongoing_captures_.erase(capture_source_->GetSourceId());
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT,
+ /*frame=*/nullptr);
+ RecordWgcCapturerResult(WgcCapturerResult::kGetFrameFailure);
+ return;
+ }
+
+ if (!frame) {
+ callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_TEMPORARY,
+ /*frame=*/nullptr);
+ RecordWgcCapturerResult(WgcCapturerResult::kFrameDropped);
+ return;
+ }
+
+ int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec;
+ RTC_HISTOGRAM_COUNTS_1000("WebRTC.DesktopCapture.Win.WgcCapturerFrameTime",
+ capture_time_ms);
+ frame->set_capture_time_ms(capture_time_ms);
+ frame->set_capturer_id(DesktopCapturerId::kWgcCapturerWin);
+ frame->set_may_contain_cursor(true);
+ frame->set_top_left(capture_source_->GetTopLeft());
+ RecordWgcCapturerResult(WgcCapturerResult::kSuccess);
+ callback_->OnCaptureResult(DesktopCapturer::Result::SUCCESS,
+ std::move(frame));
+}
+
+bool WgcCapturerWin::IsSourceBeingCaptured(DesktopCapturer::SourceId id) {
+ std::map<DesktopCapturer::SourceId, WgcCaptureSession>::iterator
+ session_iter = ongoing_captures_.find(id);
+ if (session_iter == ongoing_captures_.end())
+ return false;
+
+ return session_iter->second.IsCaptureStarted();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.h b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.h
new file mode 100644
index 0000000000..d9ee9d3fc6
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURER_WIN_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURER_WIN_H_
+
+#include <DispatcherQueue.h>
+#include <d3d11.h>
+#include <wrl/client.h>
+
+#include <map>
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "modules/desktop_capture/win/wgc_capture_session.h"
+#include "modules/desktop_capture/win/wgc_capture_source.h"
+#include "modules/desktop_capture/win/window_capture_utils.h"
+
+namespace webrtc {
+
+// Checks if the WGC API is present and supported on the system.
+bool IsWgcSupported(CaptureType capture_type);
+
+// WgcCapturerWin is initialized with an implementation of this base class,
+// which it uses to find capturable sources of a particular type. This way,
+// WgcCapturerWin can remain source-agnostic.
+class SourceEnumerator {
+ public:
+ virtual ~SourceEnumerator() = default;
+
+ virtual bool FindAllSources(DesktopCapturer::SourceList* sources) = 0;
+};
+
+class WindowEnumerator final : public SourceEnumerator {
+ public:
+ explicit WindowEnumerator(bool enumerate_current_process_windows)
+ : enumerate_current_process_windows_(enumerate_current_process_windows) {}
+
+ WindowEnumerator(const WindowEnumerator&) = delete;
+ WindowEnumerator& operator=(const WindowEnumerator&) = delete;
+
+ ~WindowEnumerator() override = default;
+
+ bool FindAllSources(DesktopCapturer::SourceList* sources) override {
+ // WGC fails to capture windows with the WS_EX_TOOLWINDOW style, so we
+ // provide it as a filter to ensure windows with the style are not returned.
+ return window_capture_helper_.EnumerateCapturableWindows(
+ sources, enumerate_current_process_windows_, WS_EX_TOOLWINDOW);
+ }
+
+ private:
+ WindowCaptureHelperWin window_capture_helper_;
+ bool enumerate_current_process_windows_;
+};
+
+class ScreenEnumerator final : public SourceEnumerator {
+ public:
+ ScreenEnumerator() = default;
+
+ ScreenEnumerator(const ScreenEnumerator&) = delete;
+ ScreenEnumerator& operator=(const ScreenEnumerator&) = delete;
+
+ ~ScreenEnumerator() override = default;
+
+ bool FindAllSources(DesktopCapturer::SourceList* sources) override {
+ return webrtc::GetScreenList(sources);
+ }
+};
+
+// A capturer that uses the Window.Graphics.Capture APIs. It is suitable for
+// both window and screen capture (but only one type per instance). Consumers
+// should not instantiate this class directly, instead they should use
+// `CreateRawWindowCapturer()` or `CreateRawScreenCapturer()` to receive a
+// capturer appropriate for the type of source they want to capture.
+class WgcCapturerWin : public DesktopCapturer {
+ public:
+ WgcCapturerWin(std::unique_ptr<WgcCaptureSourceFactory> source_factory,
+ std::unique_ptr<SourceEnumerator> source_enumerator,
+ bool allow_delayed_capturable_check);
+
+ WgcCapturerWin(const WgcCapturerWin&) = delete;
+ WgcCapturerWin& operator=(const WgcCapturerWin&) = delete;
+
+ ~WgcCapturerWin() override;
+
+ static std::unique_ptr<DesktopCapturer> CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options,
+ bool allow_delayed_capturable_check = false);
+
+ static std::unique_ptr<DesktopCapturer> CreateRawScreenCapturer(
+ const DesktopCaptureOptions& options);
+
+ // DesktopCapturer interface.
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+ bool FocusOnSelectedSource() override;
+ void Start(Callback* callback) override;
+ void CaptureFrame() override;
+
+ // Used in WgcCapturerTests.
+ bool IsSourceBeingCaptured(SourceId id);
+
+ private:
+ typedef HRESULT(WINAPI* CreateDispatcherQueueControllerFunc)(
+ DispatcherQueueOptions,
+ ABI::Windows::System::IDispatcherQueueController**);
+
+ // We need to either create or ensure that someone else created a
+ // `DispatcherQueue` on the current thread so that events will be delivered
+ // on the current thread rather than an arbitrary thread. A
+ // `DispatcherQueue`'s lifetime is tied to the thread's, and we don't post
+ // any work to it, so we don't need to hold a reference.
+ bool dispatcher_queue_created_ = false;
+
+ // Statically linking to CoreMessaging.lib is disallowed in Chromium, so we
+ // load it at runtime.
+ HMODULE core_messaging_library_ = NULL;
+ CreateDispatcherQueueControllerFunc create_dispatcher_queue_controller_func_ =
+ nullptr;
+
+ // Factory to create a WgcCaptureSource for us whenever SelectSource is
+ // called. Initialized at construction with a source-specific implementation.
+ std::unique_ptr<WgcCaptureSourceFactory> source_factory_;
+
+ // The source enumerator helps us find capturable sources of the appropriate
+ // type. Initialized at construction with a source-specific implementation.
+ std::unique_ptr<SourceEnumerator> source_enumerator_;
+
+ // The WgcCaptureSource represents the source we are capturing. It tells us
+ // if the source is capturable and it creates the GraphicsCaptureItem for us.
+ std::unique_ptr<WgcCaptureSource> capture_source_;
+
+ // A map of all the sources we are capturing and the associated
+ // WgcCaptureSession. Frames for the current source (indicated via
+ // SelectSource) will be retrieved from the appropriate session when
+ // requested via CaptureFrame.
+ // This helps us efficiently capture multiple sources (e.g. when consumers
+ // are trying to display a list of available capture targets with thumbnails).
+ std::map<SourceId, WgcCaptureSession> ongoing_captures_;
+
+ // The callback that we deliver frames to, synchronously, before CaptureFrame
+ // returns.
+ Callback* callback_ = nullptr;
+
+ // WgcCaptureSource::IsCapturable is expensive to run. So, caller can
+ // delay capturable check till capture frame is called if the WgcCapturerWin
+ // is used as a fallback capturer.
+ bool allow_delayed_capturable_check_ = false;
+
+ // A Direct3D11 device that is shared amongst the WgcCaptureSessions, who
+ // require one to perform the capture.
+ Microsoft::WRL::ComPtr<::ID3D11Device> d3d11_device_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_CAPTURER_WIN_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win_unittest.cc
new file mode 100644
index 0000000000..a7b656fcfc
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_capturer_win_unittest.cc
@@ -0,0 +1,572 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/wgc_capturer_win.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/win/test_support/test_window.h"
+#include "modules/desktop_capture/win/wgc_capture_session.h"
+#include "modules/desktop_capture/win/window_capture_utils.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/task_queue_for_test.h"
+#include "rtc_base/thread.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/win/scoped_com_initializer.h"
+#include "rtc_base/win/windows_version.h"
+#include "system_wrappers/include/metrics.h"
+#include "system_wrappers/include/sleep.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+constexpr char kWindowThreadName[] = "wgc_capturer_test_window_thread";
+constexpr WCHAR kWindowTitle[] = L"WGC Capturer Test Window";
+
+constexpr char kCapturerImplHistogram[] =
+ "WebRTC.DesktopCapture.Win.DesktopCapturerImpl";
+
+constexpr char kCapturerResultHistogram[] =
+ "WebRTC.DesktopCapture.Win.WgcCapturerResult";
+constexpr int kSuccess = 0;
+constexpr int kSessionStartFailure = 4;
+
+constexpr char kCaptureSessionResultHistogram[] =
+ "WebRTC.DesktopCapture.Win.WgcCaptureSessionStartResult";
+constexpr int kSourceClosed = 1;
+
+constexpr char kCaptureTimeHistogram[] =
+ "WebRTC.DesktopCapture.Win.WgcCapturerFrameTime";
+
+// The capturer keeps `kNumBuffers` in its frame pool, so we need to request
+// that many frames to clear those out. The next frame will have the new size
+// (if the size has changed) so we will resize the frame pool at this point.
+// Then, we need to clear any frames that may have delivered to the frame pool
+// before the resize. Finally, the next frame will be guaranteed to be the new
+// size.
+constexpr int kNumCapturesToFlushBuffers =
+ WgcCaptureSession::kNumBuffers * 2 + 1;
+
+constexpr int kSmallWindowWidth = 200;
+constexpr int kSmallWindowHeight = 100;
+constexpr int kMediumWindowWidth = 300;
+constexpr int kMediumWindowHeight = 200;
+constexpr int kLargeWindowWidth = 400;
+constexpr int kLargeWindowHeight = 500;
+
+// The size of the image we capture is slightly smaller than the actual size of
+// the window.
+constexpr int kWindowWidthSubtrahend = 14;
+constexpr int kWindowHeightSubtrahend = 7;
+
+// Custom message constants so we can direct our thread to close windows and
+// quit running.
+constexpr UINT kDestroyWindow = WM_APP;
+constexpr UINT kQuitRunning = WM_APP + 1;
+
+// When testing changes to real windows, sometimes the effects (close or resize)
+// don't happen immediately, we want to keep trying until we see the effect but
+// only for a reasonable amount of time.
+constexpr int kMaxTries = 50;
+
+} // namespace
+
+class WgcCapturerWinTest : public ::testing::TestWithParam<CaptureType>,
+ public DesktopCapturer::Callback {
+ public:
+ void SetUp() override {
+ com_initializer_ =
+ std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
+ EXPECT_TRUE(com_initializer_->Succeeded());
+
+ if (!IsWgcSupported(GetParam())) {
+ RTC_LOG(LS_INFO)
+ << "Skipping WgcCapturerWinTests on unsupported platforms.";
+ GTEST_SKIP();
+ }
+ }
+
+ void SetUpForWindowCapture(int window_width = kMediumWindowWidth,
+ int window_height = kMediumWindowHeight) {
+ capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ CreateWindowOnSeparateThread(window_width, window_height);
+ StartWindowThreadMessageLoop();
+ source_id_ = GetTestWindowIdFromSourceList();
+ }
+
+ void SetUpForScreenCapture() {
+ capturer_ = WgcCapturerWin::CreateRawScreenCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ source_id_ = GetScreenIdFromSourceList();
+ }
+
+ void TearDown() override {
+ if (window_open_) {
+ CloseTestWindow();
+ }
+ }
+
+ // The window must live on a separate thread so that we can run a message pump
+ // without blocking the test thread. This is necessary if we are interested in
+ // having GraphicsCaptureItem events (i.e. the Closed event) fire, and it more
+ // closely resembles how capture works in the wild.
+ void CreateWindowOnSeparateThread(int window_width, int window_height) {
+ window_thread_ = rtc::Thread::Create();
+ window_thread_->SetName(kWindowThreadName, nullptr);
+ window_thread_->Start();
+ SendTask(window_thread_.get(), [this, window_width, window_height]() {
+ window_thread_id_ = GetCurrentThreadId();
+ window_info_ =
+ CreateTestWindow(kWindowTitle, window_height, window_width);
+ window_open_ = true;
+
+ while (!IsWindowResponding(window_info_.hwnd)) {
+ RTC_LOG(LS_INFO) << "Waiting for test window to become responsive in "
+ "WgcWindowCaptureTest.";
+ }
+
+ while (!IsWindowValidAndVisible(window_info_.hwnd)) {
+ RTC_LOG(LS_INFO) << "Waiting for test window to be visible in "
+ "WgcWindowCaptureTest.";
+ }
+ });
+
+ ASSERT_TRUE(window_thread_->RunningForTest());
+ ASSERT_FALSE(window_thread_->IsCurrent());
+ }
+
+ void StartWindowThreadMessageLoop() {
+ window_thread_->PostTask([this]() {
+ MSG msg;
+ BOOL gm;
+ while ((gm = ::GetMessage(&msg, NULL, 0, 0)) != 0 && gm != -1) {
+ ::DispatchMessage(&msg);
+ if (msg.message == kDestroyWindow) {
+ DestroyTestWindow(window_info_);
+ }
+ if (msg.message == kQuitRunning) {
+ PostQuitMessage(0);
+ }
+ }
+ });
+ }
+
+ void CloseTestWindow() {
+ ::PostThreadMessage(window_thread_id_, kDestroyWindow, 0, 0);
+ ::PostThreadMessage(window_thread_id_, kQuitRunning, 0, 0);
+ window_thread_->Stop();
+ window_open_ = false;
+ }
+
+ DesktopCapturer::SourceId GetTestWindowIdFromSourceList() {
+ // Frequently, the test window will not show up in GetSourceList because it
+ // was created too recently. Since we are confident the window will be found
+ // eventually we loop here until we find it.
+ intptr_t src_id = 0;
+ do {
+ DesktopCapturer::SourceList sources;
+ EXPECT_TRUE(capturer_->GetSourceList(&sources));
+ auto it = std::find_if(
+ sources.begin(), sources.end(),
+ [&](const DesktopCapturer::Source& src) {
+ return src.id == reinterpret_cast<intptr_t>(window_info_.hwnd);
+ });
+
+ if (it != sources.end())
+ src_id = it->id;
+ } while (src_id != reinterpret_cast<intptr_t>(window_info_.hwnd));
+
+ return src_id;
+ }
+
+ DesktopCapturer::SourceId GetScreenIdFromSourceList() {
+ DesktopCapturer::SourceList sources;
+ EXPECT_TRUE(capturer_->GetSourceList(&sources));
+ EXPECT_GT(sources.size(), 0ULL);
+ return sources[0].id;
+ }
+
+ void DoCapture(int num_captures = 1) {
+ // Capture the requested number of frames. We expect the first capture to
+ // always succeed. If we're asked for multiple frames, we do expect to see a
+ // a couple dropped frames due to resizing the window.
+ const int max_tries = num_captures == 1 ? 1 : kMaxTries;
+ int success_count = 0;
+ for (int i = 0; success_count < num_captures && i < max_tries; i++) {
+ capturer_->CaptureFrame();
+ if (result_ == DesktopCapturer::Result::ERROR_PERMANENT)
+ break;
+ if (result_ == DesktopCapturer::Result::SUCCESS)
+ success_count++;
+ }
+
+ total_successful_captures_ += success_count;
+ EXPECT_EQ(success_count, num_captures);
+ EXPECT_EQ(result_, DesktopCapturer::Result::SUCCESS);
+ EXPECT_TRUE(frame_);
+ EXPECT_GE(metrics::NumEvents(kCapturerResultHistogram, kSuccess),
+ total_successful_captures_);
+ }
+
+ void ValidateFrame(int expected_width, int expected_height) {
+ EXPECT_EQ(frame_->size().width(), expected_width - kWindowWidthSubtrahend);
+ EXPECT_EQ(frame_->size().height(),
+ expected_height - kWindowHeightSubtrahend);
+
+ // Verify the buffer contains as much data as it should.
+ int data_length = frame_->stride() * frame_->size().height();
+
+ // The first and last pixel should have the same color because they will be
+ // from the border of the window.
+ // Pixels have 4 bytes of data so the whole pixel needs a uint32_t to fit.
+ uint32_t first_pixel = static_cast<uint32_t>(*frame_->data());
+ uint32_t last_pixel = static_cast<uint32_t>(
+ *(frame_->data() + data_length - DesktopFrame::kBytesPerPixel));
+ EXPECT_EQ(first_pixel, last_pixel);
+
+ // Let's also check a pixel from the middle of the content area, which the
+ // test window will paint a consistent color for us to verify.
+ uint8_t* middle_pixel = frame_->data() + (data_length / 2);
+
+ int sub_pixel_offset = DesktopFrame::kBytesPerPixel / 4;
+ EXPECT_EQ(*middle_pixel, kTestWindowBValue);
+ middle_pixel += sub_pixel_offset;
+ EXPECT_EQ(*middle_pixel, kTestWindowGValue);
+ middle_pixel += sub_pixel_offset;
+ EXPECT_EQ(*middle_pixel, kTestWindowRValue);
+ middle_pixel += sub_pixel_offset;
+
+ // The window is opaque so we expect 0xFF for the Alpha channel.
+ EXPECT_EQ(*middle_pixel, 0xFF);
+ }
+
+ // DesktopCapturer::Callback interface
+ // The capturer synchronously invokes this method before `CaptureFrame()`
+ // returns.
+ void OnCaptureResult(DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame> frame) override {
+ result_ = result;
+ frame_ = std::move(frame);
+ }
+
+ protected:
+ std::unique_ptr<ScopedCOMInitializer> com_initializer_;
+ DWORD window_thread_id_;
+ std::unique_ptr<rtc::Thread> window_thread_;
+ WindowInfo window_info_;
+ intptr_t source_id_;
+ bool window_open_ = false;
+ DesktopCapturer::Result result_;
+ int total_successful_captures_ = 0;
+ std::unique_ptr<DesktopFrame> frame_;
+ std::unique_ptr<DesktopCapturer> capturer_;
+};
+
+TEST_P(WgcCapturerWinTest, SelectValidSource) {
+ if (GetParam() == CaptureType::kWindow) {
+ SetUpForWindowCapture();
+ } else {
+ SetUpForScreenCapture();
+ }
+
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+}
+
+TEST_P(WgcCapturerWinTest, SelectInvalidSource) {
+ if (GetParam() == CaptureType::kWindow) {
+ capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ source_id_ = kNullWindowId;
+ } else {
+ capturer_ = WgcCapturerWin::CreateRawScreenCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ source_id_ = kInvalidScreenId;
+ }
+
+ EXPECT_FALSE(capturer_->SelectSource(source_id_));
+}
+
+TEST_P(WgcCapturerWinTest, Capture) {
+ if (GetParam() == CaptureType::kWindow) {
+ SetUpForWindowCapture();
+ } else {
+ SetUpForScreenCapture();
+ }
+
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+
+ capturer_->Start(this);
+ EXPECT_GE(metrics::NumEvents(kCapturerImplHistogram,
+ DesktopCapturerId::kWgcCapturerWin),
+ 1);
+
+ DoCapture();
+ EXPECT_GT(frame_->size().width(), 0);
+ EXPECT_GT(frame_->size().height(), 0);
+}
+
+TEST_P(WgcCapturerWinTest, CaptureTime) {
+ if (GetParam() == CaptureType::kWindow) {
+ SetUpForWindowCapture();
+ } else {
+ SetUpForScreenCapture();
+ }
+
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+ capturer_->Start(this);
+
+ int64_t start_time;
+ start_time = rtc::TimeNanos();
+ capturer_->CaptureFrame();
+
+ int capture_time_ms =
+ (rtc::TimeNanos() - start_time) / rtc::kNumNanosecsPerMillisec;
+ EXPECT_EQ(result_, DesktopCapturer::Result::SUCCESS);
+ EXPECT_TRUE(frame_);
+
+ // The test may measure the time slightly differently than the capturer. So we
+ // just check if it's within 5 ms.
+ EXPECT_NEAR(frame_->capture_time_ms(), capture_time_ms, 5);
+ EXPECT_GE(
+ metrics::NumEvents(kCaptureTimeHistogram, frame_->capture_time_ms()), 1);
+}
+
+INSTANTIATE_TEST_SUITE_P(SourceAgnostic,
+ WgcCapturerWinTest,
+ ::testing::Values(CaptureType::kWindow,
+ CaptureType::kScreen));
+
+TEST(WgcCapturerNoMonitorTest, NoMonitors) {
+ ScopedCOMInitializer com_initializer(ScopedCOMInitializer::kMTA);
+ EXPECT_TRUE(com_initializer.Succeeded());
+ if (HasActiveDisplay()) {
+ RTC_LOG(LS_INFO) << "Skip WgcCapturerWinTest designed specifically for "
+ "systems with no monitors";
+ GTEST_SKIP();
+ }
+
+ // A bug in `CreateForMonitor` prevents screen capture when no displays are
+ // attached.
+ EXPECT_FALSE(IsWgcSupported(CaptureType::kScreen));
+
+ // A bug in the DWM (Desktop Window Manager) prevents it from providing image
+ // data if there are no displays attached. This was fixed in Windows 11.
+ if (rtc::rtc_win::GetVersion() < rtc::rtc_win::Version::VERSION_WIN11)
+ EXPECT_FALSE(IsWgcSupported(CaptureType::kWindow));
+ else
+ EXPECT_TRUE(IsWgcSupported(CaptureType::kWindow));
+}
+
+class WgcCapturerMonitorTest : public WgcCapturerWinTest {
+ public:
+ void SetUp() {
+ com_initializer_ =
+ std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
+ EXPECT_TRUE(com_initializer_->Succeeded());
+
+ if (!IsWgcSupported(CaptureType::kScreen)) {
+ RTC_LOG(LS_INFO)
+ << "Skipping WgcCapturerWinTests on unsupported platforms.";
+ GTEST_SKIP();
+ }
+ }
+};
+
+TEST_F(WgcCapturerMonitorTest, FocusOnMonitor) {
+ SetUpForScreenCapture();
+ EXPECT_TRUE(capturer_->SelectSource(0));
+
+ // You can't set focus on a monitor.
+ EXPECT_FALSE(capturer_->FocusOnSelectedSource());
+}
+
+TEST_F(WgcCapturerMonitorTest, CaptureAllMonitors) {
+ SetUpForScreenCapture();
+ EXPECT_TRUE(capturer_->SelectSource(kFullDesktopScreenId));
+
+ capturer_->Start(this);
+ DoCapture();
+ EXPECT_GT(frame_->size().width(), 0);
+ EXPECT_GT(frame_->size().height(), 0);
+}
+
+class WgcCapturerWindowTest : public WgcCapturerWinTest {
+ public:
+ void SetUp() {
+ com_initializer_ =
+ std::make_unique<ScopedCOMInitializer>(ScopedCOMInitializer::kMTA);
+ EXPECT_TRUE(com_initializer_->Succeeded());
+
+ if (!IsWgcSupported(CaptureType::kWindow)) {
+ RTC_LOG(LS_INFO)
+ << "Skipping WgcCapturerWinTests on unsupported platforms.";
+ GTEST_SKIP();
+ }
+ }
+};
+
+TEST_F(WgcCapturerWindowTest, FocusOnWindow) {
+ capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ window_info_ = CreateTestWindow(kWindowTitle);
+ source_id_ = GetScreenIdFromSourceList();
+
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+ EXPECT_TRUE(capturer_->FocusOnSelectedSource());
+
+ HWND hwnd = reinterpret_cast<HWND>(source_id_);
+ EXPECT_EQ(hwnd, ::GetActiveWindow());
+ EXPECT_EQ(hwnd, ::GetForegroundWindow());
+ EXPECT_EQ(hwnd, ::GetFocus());
+ DestroyTestWindow(window_info_);
+}
+
+TEST_F(WgcCapturerWindowTest, SelectMinimizedWindow) {
+ SetUpForWindowCapture();
+ MinimizeTestWindow(reinterpret_cast<HWND>(source_id_));
+ EXPECT_FALSE(capturer_->SelectSource(source_id_));
+
+ UnminimizeTestWindow(reinterpret_cast<HWND>(source_id_));
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+}
+
+TEST_F(WgcCapturerWindowTest, SelectClosedWindow) {
+ SetUpForWindowCapture();
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+
+ CloseTestWindow();
+ EXPECT_FALSE(capturer_->SelectSource(source_id_));
+}
+
+TEST_F(WgcCapturerWindowTest, UnsupportedWindowStyle) {
+ // Create a window with the WS_EX_TOOLWINDOW style, which WGC does not
+ // support.
+ window_info_ = CreateTestWindow(kWindowTitle, kMediumWindowWidth,
+ kMediumWindowHeight, WS_EX_TOOLWINDOW);
+ capturer_ = WgcCapturerWin::CreateRawWindowCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ DesktopCapturer::SourceList sources;
+ EXPECT_TRUE(capturer_->GetSourceList(&sources));
+ auto it = std::find_if(
+ sources.begin(), sources.end(), [&](const DesktopCapturer::Source& src) {
+ return src.id == reinterpret_cast<intptr_t>(window_info_.hwnd);
+ });
+
+ // We should not find the window, since we filter for unsupported styles.
+ EXPECT_EQ(it, sources.end());
+ DestroyTestWindow(window_info_);
+}
+
+TEST_F(WgcCapturerWindowTest, IncreaseWindowSizeMidCapture) {
+ SetUpForWindowCapture(kSmallWindowWidth, kSmallWindowHeight);
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+
+ capturer_->Start(this);
+ DoCapture();
+ ValidateFrame(kSmallWindowWidth, kSmallWindowHeight);
+
+ ResizeTestWindow(window_info_.hwnd, kSmallWindowWidth, kMediumWindowHeight);
+ DoCapture(kNumCapturesToFlushBuffers);
+ ValidateFrame(kSmallWindowWidth, kMediumWindowHeight);
+
+ ResizeTestWindow(window_info_.hwnd, kLargeWindowWidth, kMediumWindowHeight);
+ DoCapture(kNumCapturesToFlushBuffers);
+ ValidateFrame(kLargeWindowWidth, kMediumWindowHeight);
+}
+
+TEST_F(WgcCapturerWindowTest, ReduceWindowSizeMidCapture) {
+ SetUpForWindowCapture(kLargeWindowWidth, kLargeWindowHeight);
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+
+ capturer_->Start(this);
+ DoCapture();
+ ValidateFrame(kLargeWindowWidth, kLargeWindowHeight);
+
+ ResizeTestWindow(window_info_.hwnd, kLargeWindowWidth, kMediumWindowHeight);
+ DoCapture(kNumCapturesToFlushBuffers);
+ ValidateFrame(kLargeWindowWidth, kMediumWindowHeight);
+
+ ResizeTestWindow(window_info_.hwnd, kSmallWindowWidth, kMediumWindowHeight);
+ DoCapture(kNumCapturesToFlushBuffers);
+ ValidateFrame(kSmallWindowWidth, kMediumWindowHeight);
+}
+
+TEST_F(WgcCapturerWindowTest, MinimizeWindowMidCapture) {
+ SetUpForWindowCapture();
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+
+ capturer_->Start(this);
+
+ // Minmize the window and capture should continue but return temporary errors.
+ MinimizeTestWindow(window_info_.hwnd);
+ for (int i = 0; i < 5; ++i) {
+ capturer_->CaptureFrame();
+ EXPECT_EQ(result_, DesktopCapturer::Result::ERROR_TEMPORARY);
+ }
+
+ // Reopen the window and the capture should continue normally.
+ UnminimizeTestWindow(window_info_.hwnd);
+ DoCapture();
+ // We can't verify the window size here because the test window does not
+ // repaint itself after it is unminimized, but capturing successfully is still
+ // a good test.
+}
+
+TEST_F(WgcCapturerWindowTest, CloseWindowMidCapture) {
+ SetUpForWindowCapture();
+ EXPECT_TRUE(capturer_->SelectSource(source_id_));
+
+ capturer_->Start(this);
+ DoCapture();
+ ValidateFrame(kMediumWindowWidth, kMediumWindowHeight);
+
+ CloseTestWindow();
+
+ // We need to pump our message queue so the Closed event will be delivered to
+ // the capturer's event handler. If we are too early and the Closed event
+ // hasn't arrived yet we should keep trying until the capturer receives it and
+ // stops.
+ auto* wgc_capturer = static_cast<WgcCapturerWin*>(capturer_.get());
+ MSG msg;
+ for (int i = 0;
+ wgc_capturer->IsSourceBeingCaptured(source_id_) && i < kMaxTries; ++i) {
+ // Unlike GetMessage, PeekMessage will not hang if there are no messages in
+ // the queue.
+ PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
+ SleepMs(1);
+ }
+
+ EXPECT_FALSE(wgc_capturer->IsSourceBeingCaptured(source_id_));
+
+ // The frame pool can buffer `kNumBuffers` frames. We must consume these
+ // and then make one more call to CaptureFrame before we expect to see the
+ // failure.
+ int num_tries = 0;
+ do {
+ capturer_->CaptureFrame();
+ } while (result_ == DesktopCapturer::Result::SUCCESS &&
+ ++num_tries <= WgcCaptureSession::kNumBuffers);
+
+ EXPECT_GE(metrics::NumEvents(kCapturerResultHistogram, kSessionStartFailure),
+ 1);
+ EXPECT_GE(metrics::NumEvents(kCaptureSessionResultHistogram, kSourceClosed),
+ 1);
+ EXPECT_EQ(result_, DesktopCapturer::Result::ERROR_PERMANENT);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.cc b/third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.cc
new file mode 100644
index 0000000000..dd9009120b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.cc
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/wgc_desktop_frame.h"
+
+#include <utility>
+
+namespace webrtc {
+
+WgcDesktopFrame::WgcDesktopFrame(DesktopSize size,
+ int stride,
+ std::vector<uint8_t>&& image_data)
+ : DesktopFrame(size, stride, image_data.data(), nullptr),
+ image_data_(std::move(image_data)) {}
+
+WgcDesktopFrame::~WgcDesktopFrame() = default;
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.h b/third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.h
new file mode 100644
index 0000000000..0a671cf2f8
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/wgc_desktop_frame.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_WGC_DESKTOP_FRAME_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_WGC_DESKTOP_FRAME_H_
+
+#include <d3d11.h>
+#include <wrl/client.h>
+
+#include <memory>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+// DesktopFrame implementation used by capturers that use the
+// Windows.Graphics.Capture API.
+class WgcDesktopFrame final : public DesktopFrame {
+ public:
+ // WgcDesktopFrame receives an rvalue reference to the `image_data` vector
+ // so that it can take ownership of it (and avoid a copy).
+ WgcDesktopFrame(DesktopSize size,
+ int stride,
+ std::vector<uint8_t>&& image_data);
+
+ WgcDesktopFrame(const WgcDesktopFrame&) = delete;
+ WgcDesktopFrame& operator=(const WgcDesktopFrame&) = delete;
+
+ ~WgcDesktopFrame() override;
+
+ private:
+ std::vector<uint8_t> image_data_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_WGC_DESKTOP_FRAME_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.cc b/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.cc
new file mode 100644
index 0000000000..d58c02e17c
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.cc
@@ -0,0 +1,486 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/window_capture_utils.h"
+
+// Just for the DWMWINDOWATTRIBUTE enums (DWMWA_CLOAKED).
+#include <dwmapi.h>
+
+#include <algorithm>
+
+#include "modules/desktop_capture/win/scoped_gdi_object.h"
+#include "rtc_base/arraysize.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/string_utils.h"
+#include "rtc_base/win/windows_version.h"
+
+namespace webrtc {
+
+namespace {
+
+struct GetWindowListParams {
+ GetWindowListParams(int flags,
+ LONG ex_style_filters,
+ DesktopCapturer::SourceList* result)
+ : ignore_untitled(flags & GetWindowListFlags::kIgnoreUntitled),
+ ignore_unresponsive(flags & GetWindowListFlags::kIgnoreUnresponsive),
+ ignore_current_process_windows(
+ flags & GetWindowListFlags::kIgnoreCurrentProcessWindows),
+ ex_style_filters(ex_style_filters),
+ result(result) {}
+ const bool ignore_untitled;
+ const bool ignore_unresponsive;
+ const bool ignore_current_process_windows;
+ const LONG ex_style_filters;
+ DesktopCapturer::SourceList* const result;
+};
+
+bool IsWindowOwnedByCurrentProcess(HWND hwnd) {
+ DWORD process_id;
+ GetWindowThreadProcessId(hwnd, &process_id);
+ return process_id == GetCurrentProcessId();
+}
+
+BOOL CALLBACK GetWindowListHandler(HWND hwnd, LPARAM param) {
+ GetWindowListParams* params = reinterpret_cast<GetWindowListParams*>(param);
+ DesktopCapturer::SourceList* list = params->result;
+
+ // Skip invisible and minimized windows
+ if (!IsWindowVisible(hwnd) || IsIconic(hwnd)) {
+ return TRUE;
+ }
+
+ // Skip windows which are not presented in the taskbar,
+ // namely owned window if they don't have the app window style set
+ HWND owner = GetWindow(hwnd, GW_OWNER);
+ LONG exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
+ if (owner && !(exstyle & WS_EX_APPWINDOW)) {
+ return TRUE;
+ }
+
+ // Filter out windows that match the extended styles the caller has specified,
+ // e.g. WS_EX_TOOLWINDOW for capturers that don't support overlay windows.
+ if (exstyle & params->ex_style_filters) {
+ return TRUE;
+ }
+
+ if (params->ignore_unresponsive && !IsWindowResponding(hwnd)) {
+ return TRUE;
+ }
+
+ DesktopCapturer::Source window;
+ window.id = reinterpret_cast<WindowId>(hwnd);
+
+ DWORD pid;
+ GetWindowThreadProcessId(hwnd, &pid);
+ window.pid = static_cast<pid_t>(pid);
+
+ // GetWindowText* are potentially blocking operations if `hwnd` is
+ // owned by the current process. The APIs will send messages to the window's
+ // message loop, and if the message loop is waiting on this operation we will
+ // enter a deadlock.
+ // https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getwindowtexta#remarks
+ //
+ // To help consumers avoid this, there is a DesktopCaptureOption to ignore
+ // windows owned by the current process. Consumers should either ensure that
+ // the thread running their message loop never waits on this operation, or use
+ // the option to exclude these windows from the source list.
+ bool owned_by_current_process = IsWindowOwnedByCurrentProcess(hwnd);
+ if (owned_by_current_process && params->ignore_current_process_windows) {
+ return TRUE;
+ }
+
+ // Even if consumers request to enumerate windows owned by the current
+ // process, we should not call GetWindowText* on unresponsive windows owned by
+ // the current process because we will hang. Unfortunately, we could still
+ // hang if the window becomes unresponsive after this check, hence the option
+ // to avoid these completely.
+ if (!owned_by_current_process || IsWindowResponding(hwnd)) {
+ const size_t kTitleLength = 500;
+ WCHAR window_title[kTitleLength] = L"";
+ if (GetWindowTextLength(hwnd) != 0 &&
+ GetWindowTextW(hwnd, window_title, kTitleLength) > 0) {
+ window.title = rtc::ToUtf8(window_title);
+ }
+ }
+
+ // Skip windows when we failed to convert the title or it is empty.
+ if (params->ignore_untitled && window.title.empty())
+ return TRUE;
+
+ // Capture the window class name, to allow specific window classes to be
+ // skipped.
+ //
+ // https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-wndclassa
+ // says lpszClassName field in WNDCLASS is limited by 256 symbols, so we don't
+ // need to have a buffer bigger than that.
+ const size_t kMaxClassNameLength = 256;
+ WCHAR class_name[kMaxClassNameLength] = L"";
+ const int class_name_length =
+ GetClassNameW(hwnd, class_name, kMaxClassNameLength);
+ if (class_name_length < 1)
+ return TRUE;
+
+ // Skip Program Manager window.
+ if (wcscmp(class_name, L"Progman") == 0)
+ return TRUE;
+
+ // Skip Start button window on Windows Vista, Windows 7.
+ // On Windows 8, Windows 8.1, Windows 10 Start button is not a top level
+ // window, so it will not be examined here.
+ if (wcscmp(class_name, L"Button") == 0)
+ return TRUE;
+
+ list->push_back(window);
+
+ return TRUE;
+}
+
+} // namespace
+
+// Prefix used to match the window class for Chrome windows.
+const wchar_t kChromeWindowClassPrefix[] = L"Chrome_WidgetWin_";
+
+// The hiddgen taskbar will leave a 2 pixel margin on the screen.
+const int kHiddenTaskbarMarginOnScreen = 2;
+
+bool GetWindowRect(HWND window, DesktopRect* result) {
+ RECT rect;
+ if (!::GetWindowRect(window, &rect)) {
+ return false;
+ }
+ *result = DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+ return true;
+}
+
+bool GetCroppedWindowRect(HWND window,
+ bool avoid_cropping_border,
+ DesktopRect* cropped_rect,
+ DesktopRect* original_rect) {
+ DesktopRect window_rect;
+ if (!GetWindowRect(window, &window_rect)) {
+ return false;
+ }
+
+ if (original_rect) {
+ *original_rect = window_rect;
+ }
+ *cropped_rect = window_rect;
+
+ bool is_maximized = false;
+ if (!IsWindowMaximized(window, &is_maximized)) {
+ return false;
+ }
+
+ // As of Windows8, transparent resize borders are added by the OS at
+ // left/bottom/right sides of a resizeable window. If the cropped window
+ // doesn't remove these borders, the background will be exposed a bit.
+ if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN8 ||
+ is_maximized) {
+ // Only apply this cropping to windows with a resize border (otherwise,
+ // it'd clip the edges of captured pop-up windows without this border).
+ LONG style = GetWindowLong(window, GWL_STYLE);
+ if (style & WS_THICKFRAME || style & DS_MODALFRAME) {
+ int width = GetSystemMetrics(SM_CXSIZEFRAME);
+ int bottom_height = GetSystemMetrics(SM_CYSIZEFRAME);
+ const int visible_border_height = GetSystemMetrics(SM_CYBORDER);
+ int top_height = visible_border_height;
+
+ // If requested, avoid cropping the visible window border. This is used
+ // for pop-up windows to include their border, but not for the outermost
+ // window (where a partially-transparent border may expose the
+ // background a bit).
+ if (avoid_cropping_border) {
+ width = std::max(0, width - GetSystemMetrics(SM_CXBORDER));
+ bottom_height = std::max(0, bottom_height - visible_border_height);
+ top_height = 0;
+ }
+ cropped_rect->Extend(-width, -top_height, -width, -bottom_height);
+ }
+ }
+
+ return true;
+}
+
+bool GetWindowContentRect(HWND window, DesktopRect* result) {
+ if (!GetWindowRect(window, result)) {
+ return false;
+ }
+
+ RECT rect;
+ if (!::GetClientRect(window, &rect)) {
+ return false;
+ }
+
+ const int width = rect.right - rect.left;
+ // The GetClientRect() is not expected to return a larger area than
+ // GetWindowRect().
+ if (width > 0 && width < result->width()) {
+ // - GetClientRect() always set the left / top of RECT to 0. So we need to
+ // estimate the border width from GetClientRect() and GetWindowRect().
+ // - Border width of a window varies according to the window type.
+ // - GetClientRect() excludes the title bar, which should be considered as
+ // part of the content and included in the captured frame. So we always
+ // estimate the border width according to the window width.
+ // - We assume a window has same border width in each side.
+ // So we shrink half of the width difference from all four sides.
+ const int shrink = ((width - result->width()) / 2);
+ // When `shrink` is negative, DesktopRect::Extend() shrinks itself.
+ result->Extend(shrink, 0, shrink, 0);
+ // Usually this should not happen, just in case we have received a strange
+ // window, which has only left and right borders.
+ if (result->height() > shrink * 2) {
+ result->Extend(0, shrink, 0, shrink);
+ }
+ RTC_DCHECK(!result->is_empty());
+ }
+
+ return true;
+}
+
+int GetWindowRegionTypeWithBoundary(HWND window, DesktopRect* result) {
+ win::ScopedGDIObject<HRGN, win::DeleteObjectTraits<HRGN>> scoped_hrgn(
+ CreateRectRgn(0, 0, 0, 0));
+ const int region_type = GetWindowRgn(window, scoped_hrgn.Get());
+
+ if (region_type == SIMPLEREGION) {
+ RECT rect;
+ GetRgnBox(scoped_hrgn.Get(), &rect);
+ *result =
+ DesktopRect::MakeLTRB(rect.left, rect.top, rect.right, rect.bottom);
+ }
+ return region_type;
+}
+
+bool GetDcSize(HDC hdc, DesktopSize* size) {
+ win::ScopedGDIObject<HGDIOBJ, win::DeleteObjectTraits<HGDIOBJ>> scoped_hgdi(
+ GetCurrentObject(hdc, OBJ_BITMAP));
+ BITMAP bitmap;
+ memset(&bitmap, 0, sizeof(BITMAP));
+ if (GetObject(scoped_hgdi.Get(), sizeof(BITMAP), &bitmap) == 0) {
+ return false;
+ }
+ size->set(bitmap.bmWidth, bitmap.bmHeight);
+ return true;
+}
+
+bool IsWindowMaximized(HWND window, bool* result) {
+ WINDOWPLACEMENT placement;
+ memset(&placement, 0, sizeof(WINDOWPLACEMENT));
+ placement.length = sizeof(WINDOWPLACEMENT);
+ if (!::GetWindowPlacement(window, &placement)) {
+ return false;
+ }
+
+ *result = (placement.showCmd == SW_SHOWMAXIMIZED);
+ return true;
+}
+
+bool IsWindowValidAndVisible(HWND window) {
+ return IsWindow(window) && IsWindowVisible(window) && !IsIconic(window);
+}
+
+bool IsWindowResponding(HWND window) {
+ // 50ms is chosen in case the system is under heavy load, but it's also not
+ // too long to delay window enumeration considerably.
+ const UINT uTimeoutMs = 50;
+ return SendMessageTimeout(window, WM_NULL, 0, 0, SMTO_ABORTIFHUNG, uTimeoutMs,
+ nullptr);
+}
+
+bool GetWindowList(int flags,
+ DesktopCapturer::SourceList* windows,
+ LONG ex_style_filters) {
+ GetWindowListParams params(flags, ex_style_filters, windows);
+ return ::EnumWindows(&GetWindowListHandler,
+ reinterpret_cast<LPARAM>(&params)) != 0;
+}
+
+// WindowCaptureHelperWin implementation.
+WindowCaptureHelperWin::WindowCaptureHelperWin() {
+ // Try to load dwmapi.dll dynamically since it is not available on XP.
+ dwmapi_library_ = LoadLibraryW(L"dwmapi.dll");
+ if (dwmapi_library_) {
+ func_ = reinterpret_cast<DwmIsCompositionEnabledFunc>(
+ GetProcAddress(dwmapi_library_, "DwmIsCompositionEnabled"));
+ dwm_get_window_attribute_func_ =
+ reinterpret_cast<DwmGetWindowAttributeFunc>(
+ GetProcAddress(dwmapi_library_, "DwmGetWindowAttribute"));
+ }
+
+ if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN10) {
+ if (FAILED(::CoCreateInstance(__uuidof(VirtualDesktopManager), nullptr,
+ CLSCTX_ALL,
+ IID_PPV_ARGS(&virtual_desktop_manager_)))) {
+ RTC_LOG(LS_WARNING) << "Fail to create instance of VirtualDesktopManager";
+ }
+ }
+}
+
+WindowCaptureHelperWin::~WindowCaptureHelperWin() {
+ if (dwmapi_library_) {
+ FreeLibrary(dwmapi_library_);
+ }
+}
+
+bool WindowCaptureHelperWin::IsAeroEnabled() {
+ BOOL result = FALSE;
+ if (func_) {
+ func_(&result);
+ }
+ return result != FALSE;
+}
+
+// This is just a best guess of a notification window. Chrome uses the Windows
+// native framework for showing notifications. So far what we know about such a
+// window includes: no title, class name with prefix "Chrome_WidgetWin_" and
+// with certain extended styles.
+bool WindowCaptureHelperWin::IsWindowChromeNotification(HWND hwnd) {
+ const size_t kTitleLength = 32;
+ WCHAR window_title[kTitleLength];
+ GetWindowTextW(hwnd, window_title, kTitleLength);
+ if (wcsnlen_s(window_title, kTitleLength) != 0) {
+ return false;
+ }
+
+ const size_t kClassLength = 256;
+ WCHAR class_name[kClassLength];
+ const int class_name_length = GetClassNameW(hwnd, class_name, kClassLength);
+ if (class_name_length < 1 ||
+ wcsncmp(class_name, kChromeWindowClassPrefix,
+ wcsnlen_s(kChromeWindowClassPrefix, kClassLength)) != 0) {
+ return false;
+ }
+
+ const LONG exstyle = GetWindowLong(hwnd, GWL_EXSTYLE);
+ if ((exstyle & WS_EX_NOACTIVATE) && (exstyle & WS_EX_TOOLWINDOW) &&
+ (exstyle & WS_EX_TOPMOST)) {
+ return true;
+ }
+
+ return false;
+}
+
+// `content_rect` is preferred because,
+// 1. WindowCapturerWinGdi is using GDI capturer, which cannot capture DX
+// output.
+// So ScreenCapturer should be used as much as possible to avoid
+// uncapturable cases. Note: lots of new applications are using DX output
+// (hardware acceleration) to improve the performance which cannot be
+// captured by WindowCapturerWinGdi. See bug http://crbug.com/741770.
+// 2. WindowCapturerWinGdi is still useful because we do not want to expose the
+// content on other windows if the target window is covered by them.
+// 3. Shadow and borders should not be considered as "content" on other
+// windows because they do not expose any useful information.
+//
+// So we can bear the false-negative cases (target window is covered by the
+// borders or shadow of other windows, but we have not detected it) in favor
+// of using ScreenCapturer, rather than let the false-positive cases (target
+// windows is only covered by borders or shadow of other windows, but we treat
+// it as overlapping) impact the user experience.
+bool WindowCaptureHelperWin::AreWindowsOverlapping(
+ HWND hwnd,
+ HWND selected_hwnd,
+ const DesktopRect& selected_window_rect) {
+ DesktopRect content_rect;
+ if (!GetWindowContentRect(hwnd, &content_rect)) {
+ // Bail out if failed to get the window area.
+ return true;
+ }
+ content_rect.IntersectWith(selected_window_rect);
+
+ if (content_rect.is_empty()) {
+ return false;
+ }
+
+ // When the taskbar is automatically hidden, it will leave a 2 pixel margin on
+ // the screen which will overlap the maximized selected window that will use
+ // up the full screen area. Since there is no solid way to identify a hidden
+ // taskbar window, we have to make an exemption here if the overlapping is
+ // 2 x screen_width/height to a maximized window.
+ bool is_maximized = false;
+ IsWindowMaximized(selected_hwnd, &is_maximized);
+ bool overlaps_hidden_horizontal_taskbar =
+ selected_window_rect.width() == content_rect.width() &&
+ content_rect.height() == kHiddenTaskbarMarginOnScreen;
+ bool overlaps_hidden_vertical_taskbar =
+ selected_window_rect.height() == content_rect.height() &&
+ content_rect.width() == kHiddenTaskbarMarginOnScreen;
+ if (is_maximized && (overlaps_hidden_horizontal_taskbar ||
+ overlaps_hidden_vertical_taskbar)) {
+ return false;
+ }
+
+ return true;
+}
+
+bool WindowCaptureHelperWin::IsWindowOnCurrentDesktop(HWND hwnd) {
+ // Make sure the window is on the current virtual desktop.
+ if (virtual_desktop_manager_) {
+ BOOL on_current_desktop;
+ if (SUCCEEDED(virtual_desktop_manager_->IsWindowOnCurrentVirtualDesktop(
+ hwnd, &on_current_desktop)) &&
+ !on_current_desktop) {
+ return false;
+ }
+ }
+ return true;
+}
+
+bool WindowCaptureHelperWin::IsWindowVisibleOnCurrentDesktop(HWND hwnd) {
+ return IsWindowValidAndVisible(hwnd) && IsWindowOnCurrentDesktop(hwnd) &&
+ !IsWindowCloaked(hwnd);
+}
+
+// A cloaked window is composited but not visible to the user.
+// Example: Cortana or the Action Center when collapsed.
+bool WindowCaptureHelperWin::IsWindowCloaked(HWND hwnd) {
+ if (!dwm_get_window_attribute_func_) {
+ // Does not apply.
+ return false;
+ }
+
+ int res = 0;
+ if (dwm_get_window_attribute_func_(hwnd, DWMWA_CLOAKED, &res, sizeof(res)) !=
+ S_OK) {
+ // Cannot tell so assume not cloaked for backward compatibility.
+ return false;
+ }
+
+ return res != 0;
+}
+
+bool WindowCaptureHelperWin::EnumerateCapturableWindows(
+ DesktopCapturer::SourceList* results,
+ bool enumerate_current_process_windows,
+ LONG ex_style_filters) {
+ int flags = (GetWindowListFlags::kIgnoreUntitled |
+ GetWindowListFlags::kIgnoreUnresponsive);
+ if (!enumerate_current_process_windows) {
+ flags |= GetWindowListFlags::kIgnoreCurrentProcessWindows;
+ }
+
+ if (!webrtc::GetWindowList(flags, results, ex_style_filters)) {
+ return false;
+ }
+
+ for (auto it = results->begin(); it != results->end();) {
+ if (!IsWindowVisibleOnCurrentDesktop(reinterpret_cast<HWND>(it->id))) {
+ it = results->erase(it);
+ } else {
+ ++it;
+ }
+ }
+
+ return true;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.h b/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.h
new file mode 100644
index 0000000000..caea07958d
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils.h
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_WINDOW_CAPTURE_UTILS_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_WINDOW_CAPTURE_UTILS_H_
+
+#include <shlobj.h>
+#include <windows.h>
+#include <wrl/client.h>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+
+namespace webrtc {
+
+// Outputs the window rect. The returned DesktopRect is in system coordinates,
+// i.e. the primary monitor on the system always starts from (0, 0). This
+// function returns false if native APIs fail.
+bool GetWindowRect(HWND window, DesktopRect* result);
+
+// Outputs the window rect, with the left/right/bottom frame border cropped if
+// the window is maximized or has a transparent resize border.
+// `avoid_cropping_border` may be set to true to avoid cropping the visible
+// border when cropping any resize border.
+// `cropped_rect` is the cropped rect relative to the
+// desktop. `original_rect` is the original rect returned from GetWindowRect.
+// Returns true if all API calls succeeded. The returned DesktopRect is in
+// system coordinates, i.e. the primary monitor on the system always starts from
+// (0, 0). `original_rect` can be nullptr.
+//
+// TODO(zijiehe): Move this function to CroppingWindowCapturerWin after it has
+// been removed from MouseCursorMonitorWin.
+// This function should only be used by CroppingWindowCapturerWin. Instead a
+// DesktopRect CropWindowRect(const DesktopRect& rect)
+// should be added as a utility function to help CroppingWindowCapturerWin and
+// WindowCapturerWinGdi to crop out the borders or shadow according to their
+// scenarios. But this function is too generic and easy to be misused.
+bool GetCroppedWindowRect(HWND window,
+ bool avoid_cropping_border,
+ DesktopRect* cropped_rect,
+ DesktopRect* original_rect);
+
+// Retrieves the rectangle of the content area of `window`. Usually it contains
+// title bar and window client area, but borders or shadow are excluded. The
+// returned DesktopRect is in system coordinates, i.e. the primary monitor on
+// the system always starts from (0, 0). This function returns false if native
+// APIs fail.
+bool GetWindowContentRect(HWND window, DesktopRect* result);
+
+// Returns the region type of the `window` and fill `rect` with the region of
+// `window` if region type is SIMPLEREGION.
+int GetWindowRegionTypeWithBoundary(HWND window, DesktopRect* result);
+
+// Retrieves the size of the `hdc`. This function returns false if native APIs
+// fail.
+bool GetDcSize(HDC hdc, DesktopSize* size);
+
+// Retrieves whether the `window` is maximized and stores in `result`. This
+// function returns false if native APIs fail.
+bool IsWindowMaximized(HWND window, bool* result);
+
+// Checks that the HWND is for a valid window, that window's visibility state is
+// visible, and that it is not minimized.
+bool IsWindowValidAndVisible(HWND window);
+
+// Checks if a window responds to a message within 50ms.
+bool IsWindowResponding(HWND window);
+
+enum GetWindowListFlags {
+ kNone = 0x00,
+ kIgnoreUntitled = 1 << 0,
+ kIgnoreUnresponsive = 1 << 1,
+ kIgnoreCurrentProcessWindows = 1 << 2,
+};
+
+// Retrieves the list of top-level windows on the screen.
+// Some windows will be ignored:
+// - Those that are invisible or minimized.
+// - Program Manager & Start menu.
+// - [with kIgnoreUntitled] windows with no title.
+// - [with kIgnoreUnresponsive] windows that are unresponsive.
+// - [with kIgnoreCurrentProcessWindows] windows owned by the current process.
+// - Any windows with extended styles that match `ex_style_filters`.
+// Returns false if native APIs failed.
+bool GetWindowList(int flags,
+ DesktopCapturer::SourceList* windows,
+ LONG ex_style_filters = 0);
+
+typedef HRESULT(WINAPI* DwmIsCompositionEnabledFunc)(BOOL* enabled);
+typedef HRESULT(WINAPI* DwmGetWindowAttributeFunc)(HWND hwnd,
+ DWORD flag,
+ PVOID result_ptr,
+ DWORD result_size);
+class WindowCaptureHelperWin {
+ public:
+ WindowCaptureHelperWin();
+ ~WindowCaptureHelperWin();
+
+ WindowCaptureHelperWin(const WindowCaptureHelperWin&) = delete;
+ WindowCaptureHelperWin& operator=(const WindowCaptureHelperWin&) = delete;
+
+ bool IsAeroEnabled();
+ bool IsWindowChromeNotification(HWND hwnd);
+ bool AreWindowsOverlapping(HWND hwnd,
+ HWND selected_hwnd,
+ const DesktopRect& selected_window_rect);
+ bool IsWindowOnCurrentDesktop(HWND hwnd);
+ bool IsWindowVisibleOnCurrentDesktop(HWND hwnd);
+ bool IsWindowCloaked(HWND hwnd);
+
+ // The optional `ex_style_filters` parameter allows callers to provide
+ // extended window styles (e.g. WS_EX_TOOLWINDOW) and prevent windows that
+ // match from being included in `results`.
+ bool EnumerateCapturableWindows(DesktopCapturer::SourceList* results,
+ bool enumerate_current_process_windows,
+ LONG ex_style_filters = 0);
+
+ private:
+ HMODULE dwmapi_library_ = nullptr;
+ DwmIsCompositionEnabledFunc func_ = nullptr;
+ DwmGetWindowAttributeFunc dwm_get_window_attribute_func_ = nullptr;
+
+ // Only used on Win10+.
+ Microsoft::WRL::ComPtr<IVirtualDesktopManager> virtual_desktop_manager_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_WINDOW_CAPTURE_UTILS_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils_unittest.cc
new file mode 100644
index 0000000000..137440b09e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/window_capture_utils_unittest.cc
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/window_capture_utils.h"
+
+#include <winuser.h>
+
+#include <algorithm>
+#include <memory>
+#include <mutex>
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/win/test_support/test_window.h"
+#include "rtc_base/task_queue_for_test.h"
+#include "rtc_base/thread.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+const char kWindowThreadName[] = "window_capture_utils_test_thread";
+const WCHAR kWindowTitle[] = L"Window Capture Utils Test";
+
+std::unique_ptr<rtc::Thread> SetUpUnresponsiveWindow(std::mutex& mtx,
+ WindowInfo& info) {
+ std::unique_ptr<rtc::Thread> window_thread;
+ window_thread = rtc::Thread::Create();
+ window_thread->SetName(kWindowThreadName, nullptr);
+ window_thread->Start();
+
+ SendTask(window_thread.get(), [&] { info = CreateTestWindow(kWindowTitle); });
+
+ // Intentionally create a deadlock to cause the window to become unresponsive.
+ mtx.lock();
+ window_thread->PostTask([&mtx]() {
+ mtx.lock();
+ mtx.unlock();
+ });
+
+ return window_thread;
+}
+
+} // namespace
+
+TEST(WindowCaptureUtilsTest, GetWindowList) {
+ WindowInfo info = CreateTestWindow(kWindowTitle);
+ DesktopCapturer::SourceList window_list;
+ ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
+ EXPECT_GT(window_list.size(), 0ULL);
+ EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
+ [&info](DesktopCapturer::Source window) {
+ return reinterpret_cast<HWND>(window.id) ==
+ info.hwnd;
+ }),
+ window_list.end());
+ DestroyTestWindow(info);
+}
+
+TEST(WindowCaptureUtilsTest, IncludeUnresponsiveWindows) {
+ std::mutex mtx;
+ WindowInfo info;
+ std::unique_ptr<rtc::Thread> window_thread =
+ SetUpUnresponsiveWindow(mtx, info);
+
+ EXPECT_FALSE(IsWindowResponding(info.hwnd));
+
+ DesktopCapturer::SourceList window_list;
+ ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
+ EXPECT_GT(window_list.size(), 0ULL);
+ EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
+ [&info](DesktopCapturer::Source window) {
+ return reinterpret_cast<HWND>(window.id) ==
+ info.hwnd;
+ }),
+ window_list.end());
+
+ mtx.unlock();
+ SendTask(window_thread.get(), [&info]() { DestroyTestWindow(info); });
+ window_thread->Stop();
+}
+
+TEST(WindowCaptureUtilsTest, IgnoreUnresponsiveWindows) {
+ std::mutex mtx;
+ WindowInfo info;
+ std::unique_ptr<rtc::Thread> window_thread =
+ SetUpUnresponsiveWindow(mtx, info);
+
+ EXPECT_FALSE(IsWindowResponding(info.hwnd));
+
+ DesktopCapturer::SourceList window_list;
+ ASSERT_TRUE(
+ GetWindowList(GetWindowListFlags::kIgnoreUnresponsive, &window_list));
+ EXPECT_EQ(std::find_if(window_list.begin(), window_list.end(),
+ [&info](DesktopCapturer::Source window) {
+ return reinterpret_cast<HWND>(window.id) ==
+ info.hwnd;
+ }),
+ window_list.end());
+
+ mtx.unlock();
+ SendTask(window_thread.get(), [&info]() { DestroyTestWindow(info); });
+ window_thread->Stop();
+}
+
+TEST(WindowCaptureUtilsTest, IncludeUntitledWindows) {
+ WindowInfo info = CreateTestWindow(L"");
+ DesktopCapturer::SourceList window_list;
+ ASSERT_TRUE(GetWindowList(GetWindowListFlags::kNone, &window_list));
+ EXPECT_GT(window_list.size(), 0ULL);
+ EXPECT_NE(std::find_if(window_list.begin(), window_list.end(),
+ [&info](DesktopCapturer::Source window) {
+ return reinterpret_cast<HWND>(window.id) ==
+ info.hwnd;
+ }),
+ window_list.end());
+ DestroyTestWindow(info);
+}
+
+TEST(WindowCaptureUtilsTest, IgnoreUntitledWindows) {
+ WindowInfo info = CreateTestWindow(L"");
+ DesktopCapturer::SourceList window_list;
+ ASSERT_TRUE(GetWindowList(GetWindowListFlags::kIgnoreUntitled, &window_list));
+ EXPECT_EQ(std::find_if(window_list.begin(), window_list.end(),
+ [&info](DesktopCapturer::Source window) {
+ return reinterpret_cast<HWND>(window.id) ==
+ info.hwnd;
+ }),
+ window_list.end());
+ DestroyTestWindow(info);
+}
+
+TEST(WindowCaptureUtilsTest, IgnoreCurrentProcessWindows) {
+ WindowInfo info = CreateTestWindow(kWindowTitle);
+ DesktopCapturer::SourceList window_list;
+ ASSERT_TRUE(GetWindowList(GetWindowListFlags::kIgnoreCurrentProcessWindows,
+ &window_list));
+ EXPECT_EQ(std::find_if(window_list.begin(), window_list.end(),
+ [&info](DesktopCapturer::Source window) {
+ return reinterpret_cast<HWND>(window.id) ==
+ info.hwnd;
+ }),
+ window_list.end());
+ DestroyTestWindow(info);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.cc b/third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.cc
new file mode 100644
index 0000000000..6fd3a4db6e
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.cc
@@ -0,0 +1,400 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/win/window_capturer_win_gdi.h"
+
+#include <cmath>
+#include <map>
+#include <memory>
+#include <utility>
+#include <vector>
+
+#include "modules/desktop_capture/cropped_desktop_frame.h"
+#include "modules/desktop_capture/desktop_capture_metrics_helper.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame_win.h"
+#include "modules/desktop_capture/win/screen_capture_utils.h"
+#include "modules/desktop_capture/win/selected_window_context.h"
+#include "rtc_base/arraysize.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/string_utils.h"
+#include "rtc_base/time_utils.h"
+#include "rtc_base/trace_event.h"
+#include "rtc_base/win/windows_version.h"
+#include "system_wrappers/include/metrics.h"
+
+namespace webrtc {
+
+// Used to pass input/output data during the EnumWindows call to collect
+// owned/pop-up windows that should be captured.
+struct OwnedWindowCollectorContext : public SelectedWindowContext {
+ OwnedWindowCollectorContext(HWND selected_window,
+ DesktopRect selected_window_rect,
+ WindowCaptureHelperWin* window_capture_helper,
+ std::vector<HWND>* owned_windows)
+ : SelectedWindowContext(selected_window,
+ selected_window_rect,
+ window_capture_helper),
+ owned_windows(owned_windows) {}
+
+ std::vector<HWND>* owned_windows;
+};
+
+// Called via EnumWindows for each root window; adds owned/pop-up windows that
+// should be captured to a vector it's passed.
+BOOL CALLBACK OwnedWindowCollector(HWND hwnd, LPARAM param) {
+ OwnedWindowCollectorContext* context =
+ reinterpret_cast<OwnedWindowCollectorContext*>(param);
+ if (hwnd == context->selected_window()) {
+ // Windows are enumerated in top-down z-order, so we can stop enumerating
+ // upon reaching the selected window.
+ return FALSE;
+ }
+
+ // Skip windows that aren't visible pop-up windows.
+ if (!(GetWindowLong(hwnd, GWL_STYLE) & WS_POPUP) ||
+ !context->window_capture_helper()->IsWindowVisibleOnCurrentDesktop(
+ hwnd)) {
+ return TRUE;
+ }
+
+ // Owned windows that intersect the selected window should be captured.
+ if (context->IsWindowOwnedBySelectedWindow(hwnd) &&
+ context->IsWindowOverlappingSelectedWindow(hwnd)) {
+ // Skip windows that draw shadows around menus. These "SysShadow" windows
+ // would otherwise be captured as solid black bars with no transparency
+ // gradient (since this capturer doesn't detect / respect variations in the
+ // window alpha channel). Any other semi-transparent owned windows will be
+ // captured fully-opaque. This seems preferable to excluding them (at least
+ // when they have content aside from a solid fill color / visual adornment;
+ // e.g. some tooltips have the transparent style set).
+ if (GetWindowLong(hwnd, GWL_EXSTYLE) & WS_EX_TRANSPARENT) {
+ const WCHAR kSysShadow[] = L"SysShadow";
+ const size_t kClassLength = arraysize(kSysShadow);
+ WCHAR class_name[kClassLength];
+ const int class_name_length =
+ GetClassNameW(hwnd, class_name, kClassLength);
+ if (class_name_length == kClassLength - 1 &&
+ wcscmp(class_name, kSysShadow) == 0) {
+ return TRUE;
+ }
+ }
+
+ context->owned_windows->push_back(hwnd);
+ }
+
+ return TRUE;
+}
+
+WindowCapturerWinGdi::WindowCapturerWinGdi(
+ bool enumerate_current_process_windows)
+ : enumerate_current_process_windows_(enumerate_current_process_windows) {}
+WindowCapturerWinGdi::~WindowCapturerWinGdi() {}
+
+bool WindowCapturerWinGdi::GetSourceList(SourceList* sources) {
+ if (!window_capture_helper_.EnumerateCapturableWindows(
+ sources, enumerate_current_process_windows_))
+ return false;
+
+ std::map<HWND, DesktopSize> new_map;
+ for (const auto& item : *sources) {
+ HWND hwnd = reinterpret_cast<HWND>(item.id);
+ new_map[hwnd] = window_size_map_[hwnd];
+ }
+ window_size_map_.swap(new_map);
+
+ return true;
+}
+
+bool WindowCapturerWinGdi::SelectSource(SourceId id) {
+ HWND window = reinterpret_cast<HWND>(id);
+ if (!IsWindowValidAndVisible(window))
+ return false;
+
+ window_ = window;
+ // When a window is not in the map, window_size_map_[window] will create an
+ // item with DesktopSize (0, 0).
+ previous_size_ = window_size_map_[window];
+ return true;
+}
+
+bool WindowCapturerWinGdi::FocusOnSelectedSource() {
+ if (!window_)
+ return false;
+
+ if (!IsWindowValidAndVisible(window_))
+ return false;
+
+ return BringWindowToTop(window_) && SetForegroundWindow(window_);
+}
+
+bool WindowCapturerWinGdi::IsOccluded(const DesktopVector& pos) {
+ DesktopVector sys_pos = pos.add(GetFullscreenRect().top_left());
+ HWND hwnd =
+ reinterpret_cast<HWND>(window_finder_.GetWindowUnderPoint(sys_pos));
+
+ return hwnd != window_ &&
+ std::find(owned_windows_.begin(), owned_windows_.end(), hwnd) ==
+ owned_windows_.end();
+}
+
+void WindowCapturerWinGdi::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+ RecordCapturerImpl(DesktopCapturerId::kWindowCapturerWinGdi);
+
+ callback_ = callback;
+}
+
+void WindowCapturerWinGdi::CaptureFrame() {
+ RTC_DCHECK(callback_);
+ int64_t capture_start_time_nanos = rtc::TimeNanos();
+
+ CaptureResults results = CaptureFrame(/*capture_owned_windows*/ true);
+ if (!results.frame) {
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+
+ int capture_time_ms = (rtc::TimeNanos() - capture_start_time_nanos) /
+ rtc::kNumNanosecsPerMillisec;
+ RTC_HISTOGRAM_COUNTS_1000(
+ "WebRTC.DesktopCapture.Win.WindowGdiCapturerFrameTime", capture_time_ms);
+ results.frame->set_capture_time_ms(capture_time_ms);
+ results.frame->set_capturer_id(DesktopCapturerId::kWindowCapturerWinGdi);
+ callback_->OnCaptureResult(results.result, std::move(results.frame));
+}
+
+WindowCapturerWinGdi::CaptureResults WindowCapturerWinGdi::CaptureFrame(
+ bool capture_owned_windows) {
+ TRACE_EVENT0("webrtc", "WindowCapturerWinGdi::CaptureFrame");
+
+ if (!window_) {
+ RTC_LOG(LS_ERROR) << "Window hasn't been selected: " << GetLastError();
+ return {Result::ERROR_PERMANENT, nullptr};
+ }
+
+ // Stop capturing if the window has been closed.
+ if (!IsWindow(window_)) {
+ RTC_LOG(LS_ERROR) << "Target window has been closed.";
+ return {Result::ERROR_PERMANENT, nullptr};
+ }
+
+ // Determine the window region excluding any resize border, and including
+ // any visible border if capturing an owned window / dialog. (Don't include
+ // any visible border for the selected window for consistency with
+ // CroppingWindowCapturerWin, which would expose a bit of the background
+ // through the partially-transparent border.)
+ const bool avoid_cropping_border = !capture_owned_windows;
+ DesktopRect cropped_rect;
+ DesktopRect original_rect;
+
+ if (!GetCroppedWindowRect(window_, avoid_cropping_border, &cropped_rect,
+ &original_rect)) {
+ RTC_LOG(LS_WARNING) << "Failed to get drawable window area: "
+ << GetLastError();
+ return {Result::ERROR_TEMPORARY, nullptr};
+ }
+
+ // Return a 1x1 black frame if the window is minimized or invisible on current
+ // desktop, to match behavior on mace. Window can be temporarily invisible
+ // during the transition of full screen mode on/off.
+ if (original_rect.is_empty() ||
+ !window_capture_helper_.IsWindowVisibleOnCurrentDesktop(window_)) {
+ std::unique_ptr<DesktopFrame> frame(
+ new BasicDesktopFrame(DesktopSize(1, 1)));
+
+ previous_size_ = frame->size();
+ window_size_map_[window_] = previous_size_;
+ return {Result::SUCCESS, std::move(frame)};
+ }
+
+ HDC window_dc = GetWindowDC(window_);
+ if (!window_dc) {
+ RTC_LOG(LS_WARNING) << "Failed to get window DC: " << GetLastError();
+ return {Result::ERROR_TEMPORARY, nullptr};
+ }
+
+ DesktopRect unscaled_cropped_rect = cropped_rect;
+ double horizontal_scale = 1.0;
+ double vertical_scale = 1.0;
+
+ DesktopSize window_dc_size;
+ if (GetDcSize(window_dc, &window_dc_size)) {
+ // The `window_dc_size` is used to detect the scaling of the original
+ // window. If the application does not support high-DPI settings, it will
+ // be scaled by Windows according to the scaling setting.
+ // https://www.google.com/search?q=windows+scaling+settings&ie=UTF-8
+ // So the size of the `window_dc`, i.e. the bitmap we can retrieve from
+ // PrintWindow() or BitBlt() function, will be smaller than
+ // `original_rect` and `cropped_rect`. Part of the captured desktop frame
+ // will be black. See
+ // bug https://bugs.chromium.org/p/webrtc/issues/detail?id=8112 for
+ // details.
+
+ // If `window_dc_size` is smaller than `window_rect`, let's resize both
+ // `original_rect` and `cropped_rect` according to the scaling factor.
+ // This will adjust the width and height of the two rects.
+ horizontal_scale =
+ static_cast<double>(window_dc_size.width()) / original_rect.width();
+ vertical_scale =
+ static_cast<double>(window_dc_size.height()) / original_rect.height();
+ original_rect.Scale(horizontal_scale, vertical_scale);
+ cropped_rect.Scale(horizontal_scale, vertical_scale);
+
+ // Translate `cropped_rect` to the left so that its position within
+ // `original_rect` remains accurate after scaling.
+ // See crbug.com/1083527 for more info.
+ int translate_left = static_cast<int>(std::round(
+ (cropped_rect.left() - original_rect.left()) * (horizontal_scale - 1)));
+ int translate_top = static_cast<int>(std::round(
+ (cropped_rect.top() - original_rect.top()) * (vertical_scale - 1)));
+ cropped_rect.Translate(translate_left, translate_top);
+ }
+
+ std::unique_ptr<DesktopFrameWin> frame(
+ DesktopFrameWin::Create(original_rect.size(), nullptr, window_dc));
+ if (!frame.get()) {
+ RTC_LOG(LS_WARNING) << "Failed to create frame.";
+ ReleaseDC(window_, window_dc);
+ return {Result::ERROR_TEMPORARY, nullptr};
+ }
+
+ HDC mem_dc = CreateCompatibleDC(window_dc);
+ HGDIOBJ previous_object = SelectObject(mem_dc, frame->bitmap());
+ BOOL result = FALSE;
+
+ // When desktop composition (Aero) is enabled each window is rendered to a
+ // private buffer allowing BitBlt() to get the window content even if the
+ // window is occluded. PrintWindow() is slower but lets rendering the window
+ // contents to an off-screen device context when Aero is not available.
+ // PrintWindow() is not supported by some applications.
+ //
+ // If Aero is enabled, we prefer BitBlt() because it's faster and avoids
+ // window flickering. Otherwise, we prefer PrintWindow() because BitBlt() may
+ // render occluding windows on top of the desired window.
+ //
+ // When composition is enabled the DC returned by GetWindowDC() doesn't always
+ // have window frame rendered correctly. Windows renders it only once and then
+ // caches the result between captures. We hack it around by calling
+ // PrintWindow() whenever window size changes, including the first time of
+ // capturing - it somehow affects what we get from BitBlt() on the subsequent
+ // captures.
+ //
+ // For Windows 8.1 and later, we want to always use PrintWindow when the
+ // cropping screen capturer falls back to the window capturer. I.e.
+ // on Windows 8.1 and later, PrintWindow is only used when the window is
+ // occluded. When the window is not occluded, it is much faster to capture
+ // the screen and to crop it to the window position and size.
+ if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN8) {
+ // Special flag that makes PrintWindow to work on Windows 8.1 and later.
+ // Indeed certain apps (e.g. those using DirectComposition rendering) can't
+ // be captured using BitBlt or PrintWindow without this flag. Note that on
+ // Windows 8.0 this flag is not supported so the block below will fallback
+ // to the other call to PrintWindow. It seems to be very tricky to detect
+ // Windows 8.0 vs 8.1 so a try/fallback is more approriate here.
+ const UINT flags = PW_RENDERFULLCONTENT;
+ result = PrintWindow(window_, mem_dc, flags);
+ }
+
+ if (!result && (!window_capture_helper_.IsAeroEnabled() ||
+ !previous_size_.equals(frame->size()))) {
+ result = PrintWindow(window_, mem_dc, 0);
+ }
+
+ // Aero is enabled or PrintWindow() failed, use BitBlt.
+ if (!result) {
+ result = BitBlt(mem_dc, 0, 0, frame->size().width(), frame->size().height(),
+ window_dc, 0, 0, SRCCOPY);
+ }
+
+ SelectObject(mem_dc, previous_object);
+ DeleteDC(mem_dc);
+ ReleaseDC(window_, window_dc);
+
+ previous_size_ = frame->size();
+ window_size_map_[window_] = previous_size_;
+
+ frame->mutable_updated_region()->SetRect(
+ DesktopRect::MakeSize(frame->size()));
+ frame->set_top_left(
+ original_rect.top_left().subtract(GetFullscreenRect().top_left()));
+
+ if (!result) {
+ RTC_LOG(LS_ERROR) << "Both PrintWindow() and BitBlt() failed.";
+ return {Result::ERROR_TEMPORARY, nullptr};
+ }
+
+ // Rect for the data is relative to the first pixel of the frame.
+ cropped_rect.Translate(-original_rect.left(), -original_rect.top());
+ std::unique_ptr<DesktopFrame> cropped_frame =
+ CreateCroppedDesktopFrame(std::move(frame), cropped_rect);
+ RTC_DCHECK(cropped_frame);
+
+ if (capture_owned_windows) {
+ // If any owned/pop-up windows overlap the selected window, capture them
+ // and copy/composite their contents into the frame.
+ owned_windows_.clear();
+ OwnedWindowCollectorContext context(window_, unscaled_cropped_rect,
+ &window_capture_helper_,
+ &owned_windows_);
+
+ if (context.IsSelectedWindowValid()) {
+ EnumWindows(OwnedWindowCollector, reinterpret_cast<LPARAM>(&context));
+
+ if (!owned_windows_.empty()) {
+ if (!owned_window_capturer_) {
+ owned_window_capturer_ = std::make_unique<WindowCapturerWinGdi>(
+ enumerate_current_process_windows_);
+ }
+
+ // Owned windows are stored in top-down z-order, so this iterates in
+ // reverse to capture / draw them in bottom-up z-order
+ for (auto it = owned_windows_.rbegin(); it != owned_windows_.rend();
+ it++) {
+ HWND hwnd = *it;
+ if (owned_window_capturer_->SelectSource(
+ reinterpret_cast<SourceId>(hwnd))) {
+ CaptureResults results = owned_window_capturer_->CaptureFrame(
+ /*capture_owned_windows*/ false);
+
+ if (results.result != DesktopCapturer::Result::SUCCESS) {
+ // Simply log any error capturing an owned/pop-up window without
+ // bubbling it up to the caller (an expected error here is that
+ // the owned/pop-up window was closed; any unexpected errors won't
+ // fail the outer capture).
+ RTC_LOG(LS_INFO) << "Capturing owned window failed (previous "
+ "error/warning pertained to that)";
+ } else {
+ // Copy / composite the captured frame into the outer frame. This
+ // may no-op if they no longer intersect (if the owned window was
+ // moved outside the owner bounds since scheduled for capture.)
+ cropped_frame->CopyIntersectingPixelsFrom(
+ *results.frame, horizontal_scale, vertical_scale);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return {Result::SUCCESS, std::move(cropped_frame)};
+}
+
+// static
+std::unique_ptr<DesktopCapturer> WindowCapturerWinGdi::CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options) {
+ return std::unique_ptr<DesktopCapturer>(
+ new WindowCapturerWinGdi(options.enumerate_current_process_windows()));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.h b/third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.h
new file mode 100644
index 0000000000..bf94dfe192
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/win/window_capturer_win_gdi.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WIN_WINDOW_CAPTURER_WIN_GDI_H_
+#define MODULES_DESKTOP_CAPTURE_WIN_WINDOW_CAPTURER_WIN_GDI_H_
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/win/window_capture_utils.h"
+#include "modules/desktop_capture/window_finder_win.h"
+
+namespace webrtc {
+
+class WindowCapturerWinGdi : public DesktopCapturer {
+ public:
+ explicit WindowCapturerWinGdi(bool enumerate_current_process_windows);
+
+ // Disallow copy and assign
+ WindowCapturerWinGdi(const WindowCapturerWinGdi&) = delete;
+ WindowCapturerWinGdi& operator=(const WindowCapturerWinGdi&) = delete;
+
+ ~WindowCapturerWinGdi() override;
+
+ static std::unique_ptr<DesktopCapturer> CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options);
+
+ // DesktopCapturer interface.
+ void Start(Callback* callback) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+ bool FocusOnSelectedSource() override;
+ bool IsOccluded(const DesktopVector& pos) override;
+
+ private:
+ struct CaptureResults {
+ Result result;
+ std::unique_ptr<DesktopFrame> frame;
+ };
+
+ CaptureResults CaptureFrame(bool capture_owned_windows);
+
+ Callback* callback_ = nullptr;
+
+ // HWND and HDC for the currently selected window or nullptr if window is not
+ // selected.
+ HWND window_ = nullptr;
+
+ DesktopSize previous_size_;
+
+ WindowCaptureHelperWin window_capture_helper_;
+
+ bool enumerate_current_process_windows_;
+
+ // This map is used to avoid flickering for the case when SelectWindow() calls
+ // are interleaved with Capture() calls.
+ std::map<HWND, DesktopSize> window_size_map_;
+
+ WindowFinderWin window_finder_;
+
+ std::vector<HWND> owned_windows_;
+ std::unique_ptr<WindowCapturerWinGdi> owned_window_capturer_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WIN_WINDOW_CAPTURER_WIN_GDI_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/window_capturer_linux.cc b/third_party/libwebrtc/modules/desktop_capture/window_capturer_linux.cc
new file mode 100644
index 0000000000..b2b1e376ad
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/window_capturer_linux.cc
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2018 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+
+#if defined(WEBRTC_USE_PIPEWIRE)
+#if defined(WEBRTC_MOZILLA_BUILD)
+#include "modules/desktop_capture/linux/wayland/moz_base_capturer_pipewire.h"
+#else
+#include "modules/desktop_capture/linux/wayland/base_capturer_pipewire.h"
+#endif
+#endif // defined(WEBRTC_USE_PIPEWIRE)
+
+#if defined(WEBRTC_USE_X11)
+#include "modules/desktop_capture/linux/x11/window_capturer_x11.h"
+#endif // defined(WEBRTC_USE_X11)
+
+namespace webrtc {
+
+// static
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options) {
+#if defined(WEBRTC_USE_PIPEWIRE)
+ if (options.allow_pipewire() && DesktopCapturer::IsRunningUnderWayland()) {
+#if defined(WEBRTC_MOZILLA_BUILD)
+ return BaseCapturerPipeWire::CreateRawCapturer(options);
+#else
+ return std::make_unique<BaseCapturerPipeWire>(options);
+#endif
+ }
+#endif // defined(WEBRTC_USE_PIPEWIRE)
+
+#if defined(WEBRTC_USE_X11)
+ return WindowCapturerX11::CreateRawWindowCapturer(options);
+#else
+ return nullptr;
+#endif // defined(WEBRTC_USE_X11)
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/window_capturer_mac.mm b/third_party/libwebrtc/modules/desktop_capture/window_capturer_mac.mm
new file mode 100644
index 0000000000..882498bc34
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/window_capturer_mac.mm
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <ApplicationServices/ApplicationServices.h>
+#include <Cocoa/Cocoa.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <utility>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/mac/desktop_configuration.h"
+#include "modules/desktop_capture/mac/desktop_configuration_monitor.h"
+#include "modules/desktop_capture/mac/desktop_frame_cgimage.h"
+#include "modules/desktop_capture/mac/window_list_utils.h"
+#include "modules/desktop_capture/window_finder_mac.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/trace_event.h"
+
+namespace webrtc {
+
+namespace {
+
+// Returns true if the window exists.
+bool IsWindowValid(CGWindowID id) {
+ CFArrayRef window_id_array =
+ CFArrayCreate(nullptr, reinterpret_cast<const void**>(&id), 1, nullptr);
+ CFArrayRef window_array =
+ CGWindowListCreateDescriptionFromArray(window_id_array);
+ bool valid = window_array && CFArrayGetCount(window_array);
+ CFRelease(window_id_array);
+ CFRelease(window_array);
+
+ return valid;
+}
+
+class WindowCapturerMac : public DesktopCapturer {
+ public:
+ explicit WindowCapturerMac(
+ rtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector,
+ rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor);
+ ~WindowCapturerMac() override;
+
+ WindowCapturerMac(const WindowCapturerMac&) = delete;
+ WindowCapturerMac& operator=(const WindowCapturerMac&) = delete;
+
+ // DesktopCapturer interface.
+ void Start(Callback* callback) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+ bool FocusOnSelectedSource() override;
+ bool IsOccluded(const DesktopVector& pos) override;
+
+ private:
+ Callback* callback_ = nullptr;
+
+ // The window being captured.
+ CGWindowID window_id_ = 0;
+
+ rtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector_;
+
+ const rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
+
+ WindowFinderMac window_finder_;
+};
+
+WindowCapturerMac::WindowCapturerMac(
+ rtc::scoped_refptr<FullScreenWindowDetector> full_screen_window_detector,
+ rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor)
+ : full_screen_window_detector_(std::move(full_screen_window_detector)),
+ configuration_monitor_(std::move(configuration_monitor)),
+ window_finder_(configuration_monitor_) {}
+
+WindowCapturerMac::~WindowCapturerMac() {}
+
+bool WindowCapturerMac::GetSourceList(SourceList* sources) {
+ return webrtc::GetWindowList(sources, true, true);
+}
+
+bool WindowCapturerMac::SelectSource(SourceId id) {
+ if (!IsWindowValid(id))
+ return false;
+ window_id_ = id;
+ return true;
+}
+
+bool WindowCapturerMac::FocusOnSelectedSource() {
+ if (!window_id_)
+ return false;
+
+ CGWindowID ids[1];
+ ids[0] = window_id_;
+ CFArrayRef window_id_array =
+ CFArrayCreate(nullptr, reinterpret_cast<const void**>(&ids), 1, nullptr);
+
+ CFArrayRef window_array =
+ CGWindowListCreateDescriptionFromArray(window_id_array);
+ if (!window_array || 0 == CFArrayGetCount(window_array)) {
+ // Could not find the window. It might have been closed.
+ RTC_LOG(LS_INFO) << "Window not found";
+ CFRelease(window_id_array);
+ return false;
+ }
+
+ CFDictionaryRef window = reinterpret_cast<CFDictionaryRef>(
+ CFArrayGetValueAtIndex(window_array, 0));
+ CFNumberRef pid_ref = reinterpret_cast<CFNumberRef>(
+ CFDictionaryGetValue(window, kCGWindowOwnerPID));
+
+ int pid;
+ CFNumberGetValue(pid_ref, kCFNumberIntType, &pid);
+
+ // TODO(jiayl): this will bring the process main window to the front. We
+ // should find a way to bring only the window to the front.
+ bool result =
+ [[NSRunningApplication runningApplicationWithProcessIdentifier: pid]
+ activateWithOptions: NSApplicationActivateIgnoringOtherApps];
+
+ CFRelease(window_id_array);
+ CFRelease(window_array);
+ return result;
+}
+
+bool WindowCapturerMac::IsOccluded(const DesktopVector& pos) {
+ DesktopVector sys_pos = pos;
+ if (configuration_monitor_) {
+ auto configuration = configuration_monitor_->desktop_configuration();
+ sys_pos = pos.add(configuration.bounds.top_left());
+ }
+ return window_finder_.GetWindowUnderPoint(sys_pos) != window_id_;
+}
+
+void WindowCapturerMac::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+
+ callback_ = callback;
+}
+
+void WindowCapturerMac::CaptureFrame() {
+ TRACE_EVENT0("webrtc", "WindowCapturerMac::CaptureFrame");
+
+ if (!IsWindowValid(window_id_)) {
+ RTC_LOG(LS_ERROR) << "The window is not valid any longer.";
+ callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr);
+ return;
+ }
+
+ CGWindowID on_screen_window = window_id_;
+ if (full_screen_window_detector_) {
+ full_screen_window_detector_->UpdateWindowListIfNeeded(
+ window_id_, [](DesktopCapturer::SourceList* sources) {
+ // Not using webrtc::GetWindowList(sources, true, false)
+ // as it doesn't allow to have in the result window with
+ // empty title along with titled window owned by the same pid.
+ return webrtc::GetWindowList(
+ [sources](CFDictionaryRef window) {
+ WindowId window_id = GetWindowId(window);
+ int pid = GetWindowOwnerPid(window);
+ if (window_id != kNullWindowId) {
+ sources->push_back(DesktopCapturer::Source{window_id, pid, GetWindowTitle(window)});
+ }
+ return true;
+ },
+ true,
+ false);
+ });
+
+ CGWindowID full_screen_window = full_screen_window_detector_->FindFullScreenWindow(window_id_);
+
+ if (full_screen_window != kCGNullWindowID) on_screen_window = full_screen_window;
+ }
+
+ std::unique_ptr<DesktopFrame> frame = DesktopFrameCGImage::CreateForWindow(on_screen_window);
+ if (!frame) {
+ RTC_LOG(LS_WARNING) << "Temporarily failed to capture window.";
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+ return;
+ }
+
+ frame->mutable_updated_region()->SetRect(
+ DesktopRect::MakeSize(frame->size()));
+ frame->set_top_left(GetWindowBounds(on_screen_window).top_left());
+
+ float scale_factor = GetWindowScaleFactor(window_id_, frame->size());
+ frame->set_dpi(DesktopVector(kStandardDPI * scale_factor, kStandardDPI * scale_factor));
+
+ callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
+}
+
+} // namespace
+
+// static
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options) {
+ return std::unique_ptr<DesktopCapturer>(new WindowCapturerMac(
+ options.full_screen_window_detector(), options.configuration_monitor()));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/window_capturer_null.cc b/third_party/libwebrtc/modules/desktop_capture/window_capturer_null.cc
new file mode 100644
index 0000000000..6da2a76691
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/window_capturer_null.cc
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+
+namespace {
+
+class WindowCapturerNull : public DesktopCapturer {
+ public:
+ WindowCapturerNull();
+ ~WindowCapturerNull() override;
+
+ WindowCapturerNull(const WindowCapturerNull&) = delete;
+ WindowCapturerNull& operator=(const WindowCapturerNull&) = delete;
+
+ // DesktopCapturer interface.
+ void Start(Callback* callback) override;
+ void CaptureFrame() override;
+ bool GetSourceList(SourceList* sources) override;
+ bool SelectSource(SourceId id) override;
+
+ private:
+ Callback* callback_ = nullptr;
+};
+
+WindowCapturerNull::WindowCapturerNull() {}
+WindowCapturerNull::~WindowCapturerNull() {}
+
+bool WindowCapturerNull::GetSourceList(SourceList* sources) {
+ // Not implemented yet.
+ return false;
+}
+
+bool WindowCapturerNull::SelectSource(SourceId id) {
+ // Not implemented yet.
+ return false;
+}
+
+void WindowCapturerNull::Start(Callback* callback) {
+ RTC_DCHECK(!callback_);
+ RTC_DCHECK(callback);
+
+ callback_ = callback;
+}
+
+void WindowCapturerNull::CaptureFrame() {
+ // Not implemented yet.
+ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
+}
+
+} // namespace
+
+// static
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options) {
+ return std::unique_ptr<DesktopCapturer>(new WindowCapturerNull());
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/window_capturer_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/window_capturer_unittest.cc
new file mode 100644
index 0000000000..519c04601b
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/window_capturer_unittest.cc
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <memory>
+#include <string>
+#include <utility>
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/desktop_frame.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "rtc_base/checks.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+
+class WindowCapturerTest : public ::testing::Test,
+ public DesktopCapturer::Callback {
+ public:
+ void SetUp() override {
+ capturer_ = DesktopCapturer::CreateWindowCapturer(
+ DesktopCaptureOptions::CreateDefault());
+ ASSERT_TRUE(capturer_);
+ }
+
+ void TearDown() override {}
+
+ // DesktopCapturer::Callback interface
+ void OnCaptureResult(DesktopCapturer::Result result,
+ std::unique_ptr<DesktopFrame> frame) override {
+ frame_ = std::move(frame);
+ }
+
+ protected:
+ std::unique_ptr<DesktopCapturer> capturer_;
+ std::unique_ptr<DesktopFrame> frame_;
+};
+
+// Verify that we can enumerate windows.
+// TODO(bugs.webrtc.org/12950): Re-enable when libc++ issue is fixed
+#if defined(WEBRTC_LINUX) && defined(MEMORY_SANITIZER)
+#define MAYBE_Enumerate DISABLED_Enumerate
+#else
+#define MAYBE_Enumerate Enumerate
+#endif
+TEST_F(WindowCapturerTest, MAYBE_Enumerate) {
+ DesktopCapturer::SourceList sources;
+ EXPECT_TRUE(capturer_->GetSourceList(&sources));
+
+ // Verify that window titles are set.
+ for (auto it = sources.begin(); it != sources.end(); ++it) {
+ EXPECT_FALSE(it->title.empty());
+ }
+}
+
+// Flaky on Linux. See: crbug.com/webrtc/7830.
+// Failing on macOS 11: See bugs.webrtc.org/12801
+#if defined(WEBRTC_LINUX) || defined(WEBRTC_MAC)
+#define MAYBE_Capture DISABLED_Capture
+#else
+#define MAYBE_Capture Capture
+#endif
+// Verify we can capture a window.
+//
+// TODO(sergeyu): Currently this test just looks at the windows that already
+// exist. Ideally it should create a test window and capture from it, but there
+// is no easy cross-platform way to create new windows (potentially we could
+// have a python script showing Tk dialog, but launching code will differ
+// between platforms).
+TEST_F(WindowCapturerTest, MAYBE_Capture) {
+ DesktopCapturer::SourceList sources;
+ capturer_->Start(this);
+ EXPECT_TRUE(capturer_->GetSourceList(&sources));
+
+ // Verify that we can select and capture each window.
+ for (auto it = sources.begin(); it != sources.end(); ++it) {
+ frame_.reset();
+ if (capturer_->SelectSource(it->id)) {
+ capturer_->CaptureFrame();
+ }
+
+ // If we failed to capture a window make sure it no longer exists.
+ if (!frame_.get()) {
+ DesktopCapturer::SourceList new_list;
+ EXPECT_TRUE(capturer_->GetSourceList(&new_list));
+ for (auto new_list_it = new_list.begin(); new_list_it != new_list.end();
+ ++new_list_it) {
+ EXPECT_FALSE(it->id == new_list_it->id);
+ }
+ continue;
+ }
+
+ EXPECT_GT(frame_->size().width(), 0);
+ EXPECT_GT(frame_->size().height(), 0);
+ }
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/window_capturer_win.cc b/third_party/libwebrtc/modules/desktop_capture/window_capturer_win.cc
new file mode 100644
index 0000000000..f289746e30
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/window_capturer_win.cc
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/desktop_capture_options.h"
+#include "modules/desktop_capture/desktop_capturer.h"
+#include "modules/desktop_capture/win/window_capturer_win_gdi.h"
+
+#if defined(RTC_ENABLE_WIN_WGC)
+#include "modules/desktop_capture/blank_detector_desktop_capturer_wrapper.h"
+#include "modules/desktop_capture/fallback_desktop_capturer_wrapper.h"
+#include "modules/desktop_capture/win/wgc_capturer_win.h"
+#include "rtc_base/win/windows_version.h"
+#endif // defined(RTC_ENABLE_WIN_WGC)
+
+namespace webrtc {
+
+// static
+std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
+ const DesktopCaptureOptions& options) {
+ std::unique_ptr<DesktopCapturer> capturer(
+ WindowCapturerWinGdi::CreateRawWindowCapturer(options));
+#if defined(RTC_ENABLE_WIN_WGC)
+ if (options.allow_wgc_capturer_fallback() &&
+ rtc::rtc_win::GetVersion() >= rtc::rtc_win::Version::VERSION_WIN11) {
+ // BlankDectector capturer will send an error when it detects a failed
+ // GDI rendering, then Fallback capturer will try to capture it again with
+ // WGC.
+ capturer = std::make_unique<BlankDetectorDesktopCapturerWrapper>(
+ std::move(capturer), RgbaColor(0, 0, 0, 0),
+ /*check_per_capture*/ true);
+
+ capturer = std::make_unique<FallbackDesktopCapturerWrapper>(
+ std::move(capturer),
+ WgcCapturerWin::CreateRawWindowCapturer(
+ options, /*allow_delayed_capturable_check*/ true));
+ }
+#endif // defined(RTC_ENABLE_WIN_WGC)
+ return capturer;
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/window_finder.cc b/third_party/libwebrtc/modules/desktop_capture/window_finder.cc
new file mode 100644
index 0000000000..86127d4c05
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/window_finder.cc
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/window_finder.h"
+
+namespace webrtc {
+
+WindowFinder::Options::Options() = default;
+WindowFinder::Options::~Options() = default;
+WindowFinder::Options::Options(const WindowFinder::Options& other) = default;
+WindowFinder::Options::Options(WindowFinder::Options&& other) = default;
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/window_finder.h b/third_party/libwebrtc/modules/desktop_capture/window_finder.h
new file mode 100644
index 0000000000..99e3cce559
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/window_finder.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WINDOW_FINDER_H_
+#define MODULES_DESKTOP_CAPTURE_WINDOW_FINDER_H_
+
+#include <memory>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_capture_types.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+#include "modules/desktop_capture/mac/desktop_configuration_monitor.h"
+#endif
+
+namespace webrtc {
+
+#if defined(WEBRTC_USE_X11)
+class XAtomCache;
+#endif
+
+// An interface to return the id of the visible window under a certain point.
+class WindowFinder {
+ public:
+ WindowFinder() = default;
+ virtual ~WindowFinder() = default;
+
+ // Returns the id of the visible window under `point`. This function returns
+ // kNullWindowId if no window is under `point` and the platform does not have
+ // "root window" concept, i.e. the visible area under `point` is the desktop.
+ // `point` is always in system coordinate, i.e. the primary monitor always
+ // starts from (0, 0).
+ virtual WindowId GetWindowUnderPoint(DesktopVector point) = 0;
+
+ struct Options final {
+ Options();
+ ~Options();
+ Options(const Options& other);
+ Options(Options&& other);
+
+#if defined(WEBRTC_USE_X11)
+ XAtomCache* cache = nullptr;
+#endif
+#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
+ rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor;
+#endif
+ };
+
+ // Creates a platform-independent WindowFinder implementation. This function
+ // returns nullptr if `options` does not contain enough information or
+ // WindowFinder does not support current platform.
+ static std::unique_ptr<WindowFinder> Create(const Options& options);
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WINDOW_FINDER_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/window_finder_mac.h b/third_party/libwebrtc/modules/desktop_capture/window_finder_mac.h
new file mode 100644
index 0000000000..988dd497dd
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/window_finder_mac.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WINDOW_FINDER_MAC_H_
+#define MODULES_DESKTOP_CAPTURE_WINDOW_FINDER_MAC_H_
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/window_finder.h"
+
+namespace webrtc {
+
+class DesktopConfigurationMonitor;
+
+// The implementation of WindowFinder for Mac OSX.
+class WindowFinderMac final : public WindowFinder {
+ public:
+ explicit WindowFinderMac(
+ rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor);
+ ~WindowFinderMac() override;
+
+ // WindowFinder implementation.
+ WindowId GetWindowUnderPoint(DesktopVector point) override;
+
+ private:
+ const rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor_;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WINDOW_FINDER_MAC_H_
diff --git a/third_party/libwebrtc/modules/desktop_capture/window_finder_mac.mm b/third_party/libwebrtc/modules/desktop_capture/window_finder_mac.mm
new file mode 100644
index 0000000000..e1d0316c79
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/window_finder_mac.mm
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/window_finder_mac.h"
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <memory>
+#include <utility>
+
+#include "modules/desktop_capture/mac/desktop_configuration.h"
+#include "modules/desktop_capture/mac/desktop_configuration_monitor.h"
+#include "modules/desktop_capture/mac/window_list_utils.h"
+
+namespace webrtc {
+
+WindowFinderMac::WindowFinderMac(
+ rtc::scoped_refptr<DesktopConfigurationMonitor> configuration_monitor)
+ : configuration_monitor_(std::move(configuration_monitor)) {}
+WindowFinderMac::~WindowFinderMac() = default;
+
+WindowId WindowFinderMac::GetWindowUnderPoint(DesktopVector point) {
+ WindowId id = kNullWindowId;
+ GetWindowList(
+ [&id, point](CFDictionaryRef window) {
+ DesktopRect bounds;
+ bounds = GetWindowBounds(window);
+ if (bounds.Contains(point)) {
+ id = GetWindowId(window);
+ return false;
+ }
+ return true;
+ },
+ true,
+ true);
+ return id;
+}
+
+// static
+std::unique_ptr<WindowFinder> WindowFinder::Create(
+ const WindowFinder::Options& options) {
+ return std::make_unique<WindowFinderMac>(options.configuration_monitor);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/window_finder_unittest.cc b/third_party/libwebrtc/modules/desktop_capture/window_finder_unittest.cc
new file mode 100644
index 0000000000..ac13f124d3
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/window_finder_unittest.cc
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/window_finder.h"
+
+#include <stdint.h>
+
+#include <memory>
+
+#include "api/scoped_refptr.h"
+#include "modules/desktop_capture/desktop_geometry.h"
+#include "modules/desktop_capture/screen_drawer.h"
+#include "rtc_base/logging.h"
+#include "test/gtest.h"
+
+#if defined(WEBRTC_USE_X11)
+#include "modules/desktop_capture/linux/x11/shared_x_display.h"
+#include "modules/desktop_capture/linux/x11/x_atom_cache.h"
+#endif
+
+#if defined(WEBRTC_WIN)
+#include <windows.h>
+
+#include "modules/desktop_capture/win/window_capture_utils.h"
+#include "modules/desktop_capture/window_finder_win.h"
+#endif
+
+namespace webrtc {
+
+namespace {
+
+#if defined(WEBRTC_WIN)
+// ScreenDrawerWin does not have a message loop, so it's unresponsive to user
+// inputs. WindowFinderWin cannot detect this kind of unresponsive windows.
+// Instead, console window is used to test WindowFinderWin.
+TEST(WindowFinderTest, FindConsoleWindow) {
+ // Creates a ScreenDrawer to avoid this test from conflicting with
+ // ScreenCapturerIntegrationTest: both tests require its window to be in
+ // foreground.
+ //
+ // In ScreenCapturer related tests, this is controlled by
+ // ScreenDrawer, which has a global lock to ensure only one ScreenDrawer
+ // window is active. So even we do not use ScreenDrawer for Windows test,
+ // creating an instance can block ScreenCapturer related tests until this test
+ // finishes.
+ //
+ // Usually the test framework should take care of this "isolated test"
+ // requirement, but unfortunately WebRTC trybots do not support this.
+ std::unique_ptr<ScreenDrawer> drawer = ScreenDrawer::Create();
+ const int kMaxSize = 10000;
+ // Enlarges current console window.
+ system("mode 1000,1000");
+ const HWND console_window = GetConsoleWindow();
+ // Ensures that current console window is visible.
+ ShowWindow(console_window, SW_MAXIMIZE);
+ // Moves the window to the top-left of the display.
+ MoveWindow(console_window, 0, 0, kMaxSize, kMaxSize, true);
+
+ bool should_restore_notopmost =
+ (GetWindowLong(console_window, GWL_EXSTYLE) & WS_EX_TOPMOST) == 0;
+
+ // Brings console window to top.
+ SetWindowPos(console_window, HWND_TOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE);
+ BringWindowToTop(console_window);
+
+ bool success = false;
+ WindowFinderWin finder;
+ for (int i = 0; i < kMaxSize; i++) {
+ const DesktopVector spot(i, i);
+ const HWND id = reinterpret_cast<HWND>(finder.GetWindowUnderPoint(spot));
+ if (id == console_window) {
+ success = true;
+ break;
+ }
+ }
+ if (should_restore_notopmost)
+ SetWindowPos(console_window, HWND_NOTOPMOST, 0, 0, 0, 0,
+ SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
+
+ if (!success)
+ FAIL();
+}
+
+#else
+TEST(WindowFinderTest, FindDrawerWindow) {
+ WindowFinder::Options options;
+#if defined(WEBRTC_USE_X11)
+ std::unique_ptr<XAtomCache> cache;
+ const auto shared_x_display = SharedXDisplay::CreateDefault();
+ if (shared_x_display) {
+ cache = std::make_unique<XAtomCache>(shared_x_display->display());
+ options.cache = cache.get();
+ }
+#endif
+ std::unique_ptr<WindowFinder> finder = WindowFinder::Create(options);
+ if (!finder) {
+ RTC_LOG(LS_WARNING)
+ << "No WindowFinder implementation for current platform.";
+ return;
+ }
+
+ std::unique_ptr<ScreenDrawer> drawer = ScreenDrawer::Create();
+ if (!drawer) {
+ RTC_LOG(LS_WARNING)
+ << "No ScreenDrawer implementation for current platform.";
+ return;
+ }
+
+ if (drawer->window_id() == kNullWindowId) {
+ // TODO(zijiehe): WindowFinderTest can use a dedicated window without
+ // relying on ScreenDrawer.
+ RTC_LOG(LS_WARNING)
+ << "ScreenDrawer implementation for current platform does "
+ "create a window.";
+ return;
+ }
+
+ // ScreenDrawer may not be able to bring the window to the top. So we test
+ // several spots, at least one of them should succeed.
+ const DesktopRect region = drawer->DrawableRegion();
+ if (region.is_empty()) {
+ RTC_LOG(LS_WARNING)
+ << "ScreenDrawer::DrawableRegion() is too small for the "
+ "WindowFinderTest.";
+ return;
+ }
+
+ for (int i = 0; i < region.width(); i++) {
+ const DesktopVector spot(
+ region.left() + i, region.top() + i * region.height() / region.width());
+ const WindowId id = finder->GetWindowUnderPoint(spot);
+ if (id == drawer->window_id()) {
+ return;
+ }
+ }
+
+ FAIL();
+}
+#endif
+
+TEST(WindowFinderTest, ShouldReturnNullWindowIfSpotIsOutOfScreen) {
+ WindowFinder::Options options;
+#if defined(WEBRTC_USE_X11)
+ std::unique_ptr<XAtomCache> cache;
+ const auto shared_x_display = SharedXDisplay::CreateDefault();
+ if (shared_x_display) {
+ cache = std::make_unique<XAtomCache>(shared_x_display->display());
+ options.cache = cache.get();
+ }
+#endif
+ std::unique_ptr<WindowFinder> finder = WindowFinder::Create(options);
+ if (!finder) {
+ RTC_LOG(LS_WARNING)
+ << "No WindowFinder implementation for current platform.";
+ return;
+ }
+
+ ASSERT_EQ(kNullWindowId,
+ finder->GetWindowUnderPoint(DesktopVector(INT16_MAX, INT16_MAX)));
+ ASSERT_EQ(kNullWindowId,
+ finder->GetWindowUnderPoint(DesktopVector(INT16_MAX, INT16_MIN)));
+ ASSERT_EQ(kNullWindowId,
+ finder->GetWindowUnderPoint(DesktopVector(INT16_MIN, INT16_MAX)));
+ ASSERT_EQ(kNullWindowId,
+ finder->GetWindowUnderPoint(DesktopVector(INT16_MIN, INT16_MIN)));
+}
+
+} // namespace
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/window_finder_win.cc b/third_party/libwebrtc/modules/desktop_capture/window_finder_win.cc
new file mode 100644
index 0000000000..a8c3d39e19
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/window_finder_win.cc
@@ -0,0 +1,46 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "modules/desktop_capture/window_finder_win.h"
+
+#include <windows.h>
+
+#include <memory>
+
+namespace webrtc {
+
+WindowFinderWin::WindowFinderWin() = default;
+WindowFinderWin::~WindowFinderWin() = default;
+
+WindowId WindowFinderWin::GetWindowUnderPoint(DesktopVector point) {
+ HWND window = WindowFromPoint(POINT{point.x(), point.y()});
+ if (!window) {
+ return kNullWindowId;
+ }
+
+ // The difference between GA_ROOTOWNER and GA_ROOT can be found at
+ // https://groups.google.com/a/chromium.org/forum/#!topic/chromium-dev/Hirr_DkuZdw.
+ // In short, we should use GA_ROOT, since we only care about the root window
+ // but not the owner.
+ window = GetAncestor(window, GA_ROOT);
+ if (!window) {
+ return kNullWindowId;
+ }
+
+ return reinterpret_cast<WindowId>(window);
+}
+
+// static
+std::unique_ptr<WindowFinder> WindowFinder::Create(
+ const WindowFinder::Options& options) {
+ return std::make_unique<WindowFinderWin>();
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/desktop_capture/window_finder_win.h b/third_party/libwebrtc/modules/desktop_capture/window_finder_win.h
new file mode 100644
index 0000000000..a04e7e1aae
--- /dev/null
+++ b/third_party/libwebrtc/modules/desktop_capture/window_finder_win.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef MODULES_DESKTOP_CAPTURE_WINDOW_FINDER_WIN_H_
+#define MODULES_DESKTOP_CAPTURE_WINDOW_FINDER_WIN_H_
+
+#include "modules/desktop_capture/window_finder.h"
+
+namespace webrtc {
+
+// The implementation of WindowFinder for Windows.
+class WindowFinderWin final : public WindowFinder {
+ public:
+ WindowFinderWin();
+ ~WindowFinderWin() override;
+
+ // WindowFinder implementation.
+ WindowId GetWindowUnderPoint(DesktopVector point) override;
+};
+
+} // namespace webrtc
+
+#endif // MODULES_DESKTOP_CAPTURE_WINDOW_FINDER_WIN_H_