From e871e027e18eb44e7ab4c8e3f9e83e439bd23080 Mon Sep 17 00:00:00 2001 From: Austin Orion Date: Mon, 12 Apr 2021 17:32:43 -0700 Subject: [PATCH] Add telemetry to measure usage, perf, and errors in Desktop Capturers. As part of adding the new WgcCapturerWin implementation of the DesktopCapturer interface, we should ensure that we can measure the health and success of this new code. In order to quantify that, I've added telemetry to measure the usage of each capturer implementation, the time taken to capture a frame, and any errors that are encountered in the new implementation. I've also set the capturer id property of frames so that we can measure error rates and performance of each implementation in Chromium as well. This CL must be completed after this Chromium CL lands: 2806094: Add histograms to record new WebRTC DesktopCapturer telemetry | https://chromium-review.googlesource.com/c/chromium/src/+/2806094 Bug: webrtc:9273 Change-Id: I33b0a008568a4df4f95e705271badc3313872f17 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/214060 Commit-Queue: Austin Orion Reviewed-by: Jamie Walch Cr-Commit-Position: refs/heads/master@{#33716} --- modules/desktop_capture/BUILD.gn | 2 + .../desktop_capture_metrics_helper.cc | 60 ++++++++ .../desktop_capture_metrics_helper.h | 22 +++ .../desktop_capture/desktop_capture_types.h | 10 +- .../win/screen_capturer_win_directx.cc | 13 +- .../win/screen_capturer_win_gdi.cc | 12 +- .../win/screen_capturer_win_magnifier.cc | 14 +- .../win/wgc_capture_session.cc | 139 +++++++++++++++--- .../desktop_capture/win/wgc_capturer_win.cc | 41 ++++++ .../win/wgc_capturer_win_unittest.cc | 57 +++++++ .../win/window_capturer_win_gdi.cc | 13 ++ 11 files changed, 355 insertions(+), 28 deletions(-) create mode 100644 modules/desktop_capture/desktop_capture_metrics_helper.cc create mode 100644 modules/desktop_capture/desktop_capture_metrics_helper.h diff --git a/modules/desktop_capture/BUILD.gn b/modules/desktop_capture/BUILD.gn index 3f170fed32..4281becd68 100644 --- a/modules/desktop_capture/BUILD.gn +++ b/modules/desktop_capture/BUILD.gn @@ -326,6 +326,8 @@ rtc_library("desktop_capture_generic") { "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", diff --git a/modules/desktop_capture/desktop_capture_metrics_helper.cc b/modules/desktop_capture/desktop_capture_metrics_helper.cc new file mode 100644 index 0000000000..6b741ef4bb --- /dev/null +++ b/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(sequential_id), + static_cast(SequentialDesktopCapturerId::kMaxValue)); +} + +} // namespace webrtc diff --git a/modules/desktop_capture/desktop_capture_metrics_helper.h b/modules/desktop_capture/desktop_capture_metrics_helper.h new file mode 100644 index 0000000000..37542b84bb --- /dev/null +++ b/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 + +namespace webrtc { + +void RecordCapturerImpl(uint32_t capturer_id); + +} // namespace webrtc + +#endif // MODULES_DESKTOP_CAPTURE_DESKTOP_CAPTURE_METRICS_HELPER_H_ diff --git a/modules/desktop_capture/desktop_capture_types.h b/modules/desktop_capture/desktop_capture_types.h index 5031cbf3ac..5f9966bb6d 100644 --- a/modules/desktop_capture/desktop_capture_types.h +++ b/modules/desktop_capture/desktop_capture_types.h @@ -36,8 +36,11 @@ const ScreenId kFullDesktopScreenId = -1; const ScreenId kInvalidScreenId = -2; -// An integer to attach to each DesktopFrame to differentiate the generator of -// the frame. +// 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(a)) | (static_cast(b) << 8) | @@ -45,6 +48,9 @@ constexpr uint32_t CreateFourCC(char a, char b, char c, char d) { } 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 diff --git a/modules/desktop_capture/win/screen_capturer_win_directx.cc b/modules/desktop_capture/win/screen_capturer_win_directx.cc index df3bee8f26..1556d7c787 100644 --- a/modules/desktop_capture/win/screen_capturer_win_directx.cc +++ b/modules/desktop_capture/win/screen_capturer_win_directx.cc @@ -16,12 +16,15 @@ #include #include +#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 { @@ -106,6 +109,7 @@ ScreenCapturerWinDirectx::~ScreenCapturerWinDirectx() = default; void ScreenCapturerWinDirectx::Start(Callback* callback) { RTC_DCHECK(!callback_); RTC_DCHECK(callback); + RecordCapturerImpl(DesktopCapturerId::kScreenCapturerWinDirectx); callback_ = callback; } @@ -169,8 +173,13 @@ void ScreenCapturerWinDirectx::CaptureFrame() { case DuplicateResult::SUCCEEDED: { std::unique_ptr frame = frames_.current_frame()->frame()->Share(); - frame->set_capture_time_ms((rtc::TimeNanos() - capture_start_time_nanos) / - rtc::kNumNanosecsPerMillisec); + + 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 diff --git a/modules/desktop_capture/win/screen_capturer_win_gdi.cc b/modules/desktop_capture/win/screen_capturer_win_gdi.cc index bf6cb162a0..dc27344f82 100644 --- a/modules/desktop_capture/win/screen_capturer_win_gdi.cc +++ b/modules/desktop_capture/win/screen_capturer_win_gdi.cc @@ -12,7 +12,9 @@ #include +#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" @@ -24,6 +26,7 @@ #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 { @@ -92,8 +95,12 @@ void ScreenCapturerWinGdi::CaptureFrame() { GetDeviceCaps(desktop_dc_, LOGPIXELSY))); frame->mutable_updated_region()->SetRect( DesktopRect::MakeSize(frame->size())); - frame->set_capture_time_ms((rtc::TimeNanos() - capture_start_time_nanos) / - rtc::kNumNanosecsPerMillisec); + + 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)); } @@ -112,6 +119,7 @@ bool ScreenCapturerWinGdi::SelectSource(SourceId id) { void ScreenCapturerWinGdi::Start(Callback* callback) { RTC_DCHECK(!callback_); RTC_DCHECK(callback); + RecordCapturerImpl(DesktopCapturerId::kScreenCapturerWinGdi); callback_ = callback; diff --git a/modules/desktop_capture/win/screen_capturer_win_magnifier.cc b/modules/desktop_capture/win/screen_capturer_win_magnifier.cc index 1a7bbc18c8..214eb0e463 100644 --- a/modules/desktop_capture/win/screen_capturer_win_magnifier.cc +++ b/modules/desktop_capture/win/screen_capturer_win_magnifier.cc @@ -12,7 +12,9 @@ #include +#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" @@ -23,6 +25,7 @@ #include "rtc_base/checks.h" #include "rtc_base/logging.h" #include "rtc_base/time_utils.h" +#include "system_wrappers/include/metrics.h" namespace webrtc { @@ -62,6 +65,8 @@ ScreenCapturerWinMagnifier::~ScreenCapturerWinMagnifier() { void ScreenCapturerWinMagnifier::Start(Callback* callback) { RTC_DCHECK(!callback_); RTC_DCHECK(callback); + RecordCapturerImpl(DesktopCapturerId::kScreenCapturerWinMagnifier); + callback_ = callback; if (!InitializeMagnifier()) { @@ -115,8 +120,13 @@ void ScreenCapturerWinMagnifier::CaptureFrame() { GetDeviceCaps(desktop_dc_, LOGPIXELSY))); frame->mutable_updated_region()->SetRect( DesktopRect::MakeSize(frame->size())); - frame->set_capture_time_ms((rtc::TimeNanos() - capture_start_time_nanos) / - rtc::kNumNanosecsPerMillisec); + + 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)); } diff --git a/modules/desktop_capture/win/wgc_capture_session.cc b/modules/desktop_capture/win/wgc_capture_session.cc index 7ff2f93f95..48c56864b3 100644 --- a/modules/desktop_capture/win/wgc_capture_session.cc +++ b/modules/desktop_capture/win/wgc_capture_session.cc @@ -13,6 +13,7 @@ #include #include #include + #include #include #include @@ -20,8 +21,10 @@ #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; @@ -39,6 +42,54 @@ const auto kPixelFormat = ABI::Windows::Graphics::DirectX::DirectXPixelFormat:: // for a new frame. const int kNumBuffers = 1; +// 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, + kGetItemSizeFailed = 8, + kCreateFreeThreadedFailed = 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(error), static_cast(StartCaptureResult::kMaxValue)); +} + +void RecordGetFrameResult(GetFrameResult error) { + RTC_HISTOGRAM_ENUMERATION( + "WebRTC.DesktopCapture.Win.WgcCaptureSessionGetFrameResult", + static_cast(error), static_cast(GetFrameResult::kMaxValue)); +} + } // namespace WgcCaptureSession::WgcCaptureSession(ComPtr d3d11_device, @@ -52,6 +103,7 @@ HRESULT WgcCaptureSession::StartCapture() { if (item_closed_) { RTC_LOG(LS_ERROR) << "The target source has been closed."; + RecordStartCaptureResult(StartCaptureResult::kSourceClosed); return E_ABORT; } @@ -67,57 +119,80 @@ HRESULT WgcCaptureSession::StartCapture() { this, &WgcCaptureSession::OnItemClosed); EventRegistrationToken item_closed_token; HRESULT hr = item_->add_Closed(closed_handler.Get(), &item_closed_token); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordStartCaptureResult(StartCaptureResult::kAddClosedFailed); return hr; + } ComPtr dxgi_device; hr = d3d11_device_->QueryInterface(IID_PPV_ARGS(&dxgi_device)); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordStartCaptureResult(StartCaptureResult::kDxgiDeviceCastFailed); return hr; + } - if (!ResolveCoreWinRTDirect3DDelayload()) + if (!ResolveCoreWinRTDirect3DDelayload()) { + RecordStartCaptureResult(StartCaptureResult::kD3dDelayLoadFailed); return E_FAIL; + } hr = CreateDirect3DDeviceFromDXGIDevice(dxgi_device.Get(), &direct3d_device_); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordStartCaptureResult(StartCaptureResult::kD3dDeviceCreationFailed); return hr; + } ComPtr frame_pool_statics; hr = GetActivationFactory< ABI::Windows::Graphics::Capture::IDirect3D11CaptureFramePoolStatics, RuntimeClass_Windows_Graphics_Capture_Direct3D11CaptureFramePool>( &frame_pool_statics); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordStartCaptureResult(StartCaptureResult::kFramePoolActivationFailed); return hr; + } // Cast to FramePoolStatics2 so we can use CreateFreeThreaded and avoid the // need to have a DispatcherQueue. We don't listen for the FrameArrived event, // so there's no difference. ComPtr frame_pool_statics2; hr = frame_pool_statics->QueryInterface(IID_PPV_ARGS(&frame_pool_statics2)); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordStartCaptureResult(StartCaptureResult::kFramePoolCastFailed); return hr; + } ABI::Windows::Graphics::SizeInt32 item_size; hr = item_.Get()->get_Size(&item_size); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordStartCaptureResult(StartCaptureResult::kGetItemSizeFailed); return hr; + } previous_size_ = item_size; hr = frame_pool_statics2->CreateFreeThreaded(direct3d_device_.Get(), kPixelFormat, kNumBuffers, item_size, &frame_pool_); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordStartCaptureResult(StartCaptureResult::kCreateFreeThreadedFailed); return hr; + } hr = frame_pool_->CreateCaptureSession(item_.Get(), &session_); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordStartCaptureResult(StartCaptureResult::kCreateCaptureSessionFailed); return hr; + } hr = session_->StartCapture(); - if (FAILED(hr)) + 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; @@ -129,6 +204,7 @@ HRESULT WgcCaptureSession::GetFrame( if (item_closed_) { RTC_LOG(LS_ERROR) << "The target source has been closed."; + RecordGetFrameResult(GetFrameResult::kItemClosed); return E_ABORT; } @@ -136,35 +212,48 @@ HRESULT WgcCaptureSession::GetFrame( ComPtr capture_frame; HRESULT hr = frame_pool_->TryGetNextFrame(&capture_frame); - if (FAILED(hr)) + if (FAILED(hr)) { + RTC_LOG(LS_ERROR) << "TryGetNextFrame failed: " << hr; + RecordGetFrameResult(GetFrameResult::kTryGetNextFrameFailed); return hr; + } - if (!capture_frame) + if (!capture_frame) { + RecordGetFrameResult(GetFrameResult::kFrameDropped); return hr; + } // We need to get this CaptureFrame as an ID3D11Texture2D so that we can get // the raw image data in the format required by the DesktopFrame interface. ComPtr d3d_surface; hr = capture_frame->get_Surface(&d3d_surface); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordGetFrameResult(GetFrameResult::kGetSurfaceFailed); return hr; + } ComPtr direct3DDxgiInterfaceAccess; hr = d3d_surface->QueryInterface(IID_PPV_ARGS(&direct3DDxgiInterfaceAccess)); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordGetFrameResult(GetFrameResult::kDxgiInterfaceAccessFailed); return hr; + } ComPtr texture_2D; hr = direct3DDxgiInterfaceAccess->GetInterface(IID_PPV_ARGS(&texture_2D)); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordGetFrameResult(GetFrameResult::kTexture2dCastFailed); return hr; + } if (!mapped_texture_) { hr = CreateMappedTexture(texture_2D); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordGetFrameResult(GetFrameResult::kCreateMappedTextureFailed); return hr; + } } // We need to copy |texture_2D| into |mapped_texture_| as the latter has the @@ -178,13 +267,17 @@ HRESULT WgcCaptureSession::GetFrame( 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)) + if (FAILED(hr)) { + RecordGetFrameResult(GetFrameResult::kMapFrameFailed); return hr; + } ABI::Windows::Graphics::SizeInt32 new_size; hr = capture_frame->get_ContentSize(&new_size); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordGetFrameResult(GetFrameResult::kGetContentSizeFailed); 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 @@ -217,15 +310,21 @@ HRESULT WgcCaptureSession::GetFrame( if (previous_size_.Height != new_size.Height || previous_size_.Width != new_size.Width) { hr = CreateMappedTexture(texture_2D, new_size.Width, new_size.Height); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordGetFrameResult(GetFrameResult::kResizeMappedTextureFailed); return hr; + } hr = frame_pool_->Recreate(direct3d_device_.Get(), kPixelFormat, kNumBuffers, new_size); - if (FAILED(hr)) + if (FAILED(hr)) { + RecordGetFrameResult(GetFrameResult::kRecreateFramePoolFailed); return hr; + } } + RecordGetFrameResult(GetFrameResult::kSuccess); + previous_size_ = new_size; return hr; } diff --git a/modules/desktop_capture/win/wgc_capturer_win.cc b/modules/desktop_capture/win/wgc_capturer_win.cc index 4c5ca29dc3..0b4d9740d2 100644 --- a/modules/desktop_capture/win/wgc_capturer_win.cc +++ b/modules/desktop_capture/win/wgc_capturer_win.cc @@ -12,14 +12,39 @@ #include +#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 "system_wrappers/include/metrics.h" namespace WGC = ABI::Windows::Graphics::Capture; using Microsoft::WRL::ComPtr; namespace webrtc { +namespace { + +enum class WgcCapturerResult { + kSuccess = 0, + kNoDirect3dDevice = 1, + kNoSourceSelected = 2, + kItemCreationFailure = 3, + kSessionStartFailure = 4, + kGetFrameFailure = 5, + kFrameDropped = 6, + kMaxValue = kFrameDropped +}; + +void RecordWgcCapturerResult(WgcCapturerResult error) { + RTC_HISTOGRAM_ENUMERATION("WebRTC.DesktopCapture.Win.WgcCapturerResult", + static_cast(error), + static_cast(WgcCapturerResult::kMaxValue)); +} + +} // namespace + WgcCapturerWin::WgcCapturerWin( std::unique_ptr source_factory, std::unique_ptr source_enumerator) @@ -55,6 +80,7 @@ bool WgcCapturerWin::SelectSource(DesktopCapturer::SourceId id) { void WgcCapturerWin::Start(Callback* callback) { RTC_DCHECK(!callback_); RTC_DCHECK(callback); + RecordCapturerImpl(DesktopCapturerId::kWgcCapturerWin); callback_ = callback; @@ -89,6 +115,7 @@ void WgcCapturerWin::CaptureFrame() { RTC_LOG(LS_ERROR) << "Source hasn't been selected"; callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, /*frame=*/nullptr); + RecordWgcCapturerResult(WgcCapturerResult::kNoSourceSelected); return; } @@ -96,9 +123,12 @@ void WgcCapturerWin::CaptureFrame() { RTC_LOG(LS_ERROR) << "No D3D11D3evice, cannot capture."; callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, /*frame=*/nullptr); + RecordWgcCapturerResult(WgcCapturerResult::kNoDirect3dDevice); return; } + int64_t capture_start_time_nanos = rtc::TimeNanos(); + HRESULT hr; WgcCaptureSession* capture_session = nullptr; std::map::iterator session_iter = @@ -110,6 +140,7 @@ void WgcCapturerWin::CaptureFrame() { RTC_LOG(LS_ERROR) << "Failed to create a GraphicsCaptureItem: " << hr; callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, /*frame=*/nullptr); + RecordWgcCapturerResult(WgcCapturerResult::kItemCreationFailure); return; } @@ -131,6 +162,7 @@ void WgcCapturerWin::CaptureFrame() { ongoing_captures_.erase(capture_source_->GetSourceId()); callback_->OnCaptureResult(DesktopCapturer::Result::ERROR_PERMANENT, /*frame=*/nullptr); + RecordWgcCapturerResult(WgcCapturerResult::kSessionStartFailure); return; } } @@ -142,15 +174,24 @@ void WgcCapturerWin::CaptureFrame() { 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); + RecordWgcCapturerResult(WgcCapturerResult::kSuccess); callback_->OnCaptureResult(DesktopCapturer::Result::SUCCESS, std::move(frame)); } diff --git a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc index 25866c22db..732de7db20 100644 --- a/modules/desktop_capture/win/wgc_capturer_win_unittest.cc +++ b/modules/desktop_capture/win/wgc_capturer_win_unittest.cc @@ -22,8 +22,10 @@ #include "rtc_base/checks.h" #include "rtc_base/logging.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 "test/gtest.h" namespace webrtc { @@ -32,6 +34,21 @@ namespace { const char kWindowThreadName[] = "wgc_capturer_test_window_thread"; const WCHAR kWindowTitle[] = L"WGC Capturer Test Window"; +const char kCapturerImplHistogram[] = + "WebRTC.DesktopCapture.Win.DesktopCapturerImpl"; + +const char kCapturerResultHistogram[] = + "WebRTC.DesktopCapture.Win.WgcCapturerResult"; +const int kSuccess = 0; +const int kSessionStartFailure = 4; + +const char kCaptureSessionResultHistogram[] = + "WebRTC.DesktopCapture.Win.WgcCaptureSessionStartResult"; +const int kSourceClosed = 1; + +const char kCaptureTimeHistogram[] = + "WebRTC.DesktopCapture.Win.WgcCapturerFrameTime"; + const int kSmallWindowWidth = 200; const int kSmallWindowHeight = 100; const int kMediumWindowWidth = 300; @@ -181,6 +198,10 @@ class WgcCapturerWinTest : public ::testing::TestWithParam, EXPECT_EQ(result_, DesktopCapturer::Result::SUCCESS); EXPECT_TRUE(frame_); + + EXPECT_GT(metrics::NumEvents(kCapturerResultHistogram, kSuccess), + successful_captures_); + ++successful_captures_; } void ValidateFrame(int expected_width, int expected_height) { @@ -233,6 +254,7 @@ class WgcCapturerWinTest : public ::testing::TestWithParam, intptr_t source_id_; bool window_open_ = false; DesktopCapturer::Result result_; + int successful_captures_ = 0; std::unique_ptr frame_; std::unique_ptr capturer_; }; @@ -271,11 +293,42 @@ TEST_P(WgcCapturerWinTest, Capture) { 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::kWindowCapture) { + SetUpForWindowCapture(); + } else { + SetUpForScreenCapture(); + } + + EXPECT_TRUE(capturer_->SelectSource(source_id_)); + capturer_->Start(this); + + int64_t start_time; + do { + start_time = rtc::TimeNanos(); + capturer_->CaptureFrame(); + } while (result_ == DesktopCapturer::Result::ERROR_TEMPORARY); + + int capture_time_ms = + (rtc::TimeNanos() - start_time) / rtc::kNumNanosecsPerMillisec; + 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::kWindowCapture, @@ -403,6 +456,10 @@ TEST_F(WgcCapturerWinTest, CloseWindowMidCapture) { if (result_ == DesktopCapturer::Result::SUCCESS) capturer_->CaptureFrame(); + EXPECT_GE(metrics::NumEvents(kCapturerResultHistogram, kSessionStartFailure), + 1); + EXPECT_GE(metrics::NumEvents(kCaptureSessionResultHistogram, kSourceClosed), + 1); EXPECT_EQ(result_, DesktopCapturer::Result::ERROR_PERMANENT); } diff --git a/modules/desktop_capture/win/window_capturer_win_gdi.cc b/modules/desktop_capture/win/window_capturer_win_gdi.cc index 04cd7f667d..5f7c7885ca 100644 --- a/modules/desktop_capture/win/window_capturer_win_gdi.cc +++ b/modules/desktop_capture/win/window_capturer_win_gdi.cc @@ -17,6 +17,8 @@ #include #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" @@ -25,8 +27,10 @@ #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/win32.h" +#include "system_wrappers/include/metrics.h" namespace webrtc { @@ -143,14 +147,23 @@ bool WindowCapturerWinGdi::IsOccluded(const DesktopVector& pos) { 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); + + 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)); }