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)); }