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 <auorion@microsoft.com> Reviewed-by: Jamie Walch <jamiewalch@chromium.org> Cr-Commit-Position: refs/heads/master@{#33716}
This commit is contained in:
parent
efcfa4b94d
commit
e871e027e1
@ -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",
|
||||
|
||||
60
modules/desktop_capture/desktop_capture_metrics_helper.cc
Normal file
60
modules/desktop_capture/desktop_capture_metrics_helper.cc
Normal file
@ -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
|
||||
22
modules/desktop_capture/desktop_capture_metrics_helper.h
Normal file
22
modules/desktop_capture/desktop_capture_metrics_helper.h
Normal file
@ -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_
|
||||
@ -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<uint32_t>(a)) | (static_cast<uint32_t>(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
|
||||
|
||||
@ -16,12 +16,15 @@
|
||||
#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 {
|
||||
|
||||
@ -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<DesktopFrame> 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
|
||||
|
||||
@ -12,7 +12,9 @@
|
||||
|
||||
#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"
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -12,7 +12,9 @@
|
||||
|
||||
#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"
|
||||
@ -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));
|
||||
}
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include <windows.graphics.capture.interop.h>
|
||||
#include <windows.graphics.directX.direct3d11.interop.h>
|
||||
#include <wrl.h>
|
||||
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
@ -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<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,
|
||||
@ -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<IDXGIDevice> 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<WGC::IDirect3D11CaptureFramePoolStatics> 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<WGC::IDirect3D11CaptureFramePoolStatics2> 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<WGC::IDirect3D11CaptureFrame> 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<ABI::Windows::Graphics::DirectX::Direct3D11::IDirect3DSurface>
|
||||
d3d_surface;
|
||||
hr = capture_frame->get_Surface(&d3d_surface);
|
||||
if (FAILED(hr))
|
||||
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))
|
||||
if (FAILED(hr)) {
|
||||
RecordGetFrameResult(GetFrameResult::kDxgiInterfaceAccessFailed);
|
||||
return hr;
|
||||
}
|
||||
|
||||
ComPtr<ID3D11Texture2D> 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;
|
||||
}
|
||||
|
||||
@ -12,14 +12,39 @@
|
||||
|
||||
#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 "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<int>(error),
|
||||
static_cast<int>(WgcCapturerResult::kMaxValue));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
WgcCapturerWin::WgcCapturerWin(
|
||||
std::unique_ptr<WgcCaptureSourceFactory> source_factory,
|
||||
std::unique_ptr<SourceEnumerator> 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<SourceId, WgcCaptureSession>::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));
|
||||
}
|
||||
|
||||
@ -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<CaptureType>,
|
||||
|
||||
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<CaptureType>,
|
||||
intptr_t source_id_;
|
||||
bool window_open_ = false;
|
||||
DesktopCapturer::Result result_;
|
||||
int successful_captures_ = 0;
|
||||
std::unique_ptr<DesktopFrame> frame_;
|
||||
std::unique_ptr<DesktopCapturer> 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);
|
||||
}
|
||||
|
||||
|
||||
@ -17,6 +17,8 @@
|
||||
#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"
|
||||
@ -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));
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user