Commit 1165c605 authored by Christian Larson's avatar Christian Larson Committed by Commit Bot

Add wait logic to Init() on MF_CAPTURE_ENGINE_INITIALIZED event.

Before completing the VideoCaptureDeviceMFWin::Init() function we
should wait on MF_CAPTURE_ENGINE_INITIALIZED.  The Capture Source is
not fully constructed until the MF_CAPTURE_ENGINE_INITIALIZED event
fires and calling methods before this time can cause unstable behavior.

Bug: 1076730
Change-Id: I239077a4d9a599643b9e2c4ed0e26219a2dbb58f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2238879Reviewed-by: default avatarBruce Dawson <brucedawson@chromium.org>
Reviewed-by: default avatarGuido Urdaneta <guidou@chromium.org>
Commit-Queue: Christian Larson <chrila@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#780498}
parent 73ccef04
...@@ -683,7 +683,12 @@ VideoCaptureDeviceMFWin::VideoCaptureDeviceMFWin( ...@@ -683,7 +683,12 @@ VideoCaptureDeviceMFWin::VideoCaptureDeviceMFWin(
has_sent_on_started_to_client_(false), has_sent_on_started_to_client_(false),
exposure_mode_manual_(false), exposure_mode_manual_(false),
focus_mode_manual_(false), focus_mode_manual_(false),
white_balance_mode_manual_(false) { white_balance_mode_manual_(false),
capture_initialize_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED),
// We never want to reset |capture_error_|.
capture_error_(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED) {
DETACH_FROM_SEQUENCE(sequence_checker_); DETACH_FROM_SEQUENCE(sequence_checker_);
} }
...@@ -731,6 +736,13 @@ bool VideoCaptureDeviceMFWin::Init() { ...@@ -731,6 +736,13 @@ bool VideoCaptureDeviceMFWin::Init() {
LogError(FROM_HERE, hr); LogError(FROM_HERE, hr);
return false; return false;
} }
hr = WaitOnCaptureEvent(MF_CAPTURE_ENGINE_INITIALIZED);
if (FAILED(hr)) {
LogError(FROM_HERE, hr);
return false;
}
is_initialized_ = true; is_initialized_ = true;
return true; return true;
} }
...@@ -1320,7 +1332,21 @@ void VideoCaptureDeviceMFWin::OnEvent(IMFMediaEvent* media_event) { ...@@ -1320,7 +1332,21 @@ void VideoCaptureDeviceMFWin::OnEvent(IMFMediaEvent* media_event) {
base::AutoLock lock(lock_); base::AutoLock lock(lock_);
HRESULT hr; HRESULT hr;
GUID capture_event_guid = GUID_NULL;
media_event->GetStatus(&hr); media_event->GetStatus(&hr);
media_event->GetExtendedType(&capture_event_guid);
// TODO(http://crbug.com/1093521): Add cases for Start
// MF_CAPTURE_ENGINE_PREVIEW_STARTED and MF_CAPTURE_ENGINE_PREVIEW_STOPPED
// When MF_CAPTURE_ENGINE_ERROR is returned the captureengine object is no
// longer valid.
if (capture_event_guid == MF_CAPTURE_ENGINE_ERROR || FAILED(hr)) {
capture_error_.Signal();
// There should always be a valid error
hr = SUCCEEDED(hr) ? E_UNEXPECTED : hr;
} else if (capture_event_guid == MF_CAPTURE_ENGINE_INITIALIZED) {
capture_initialize_.Signal();
}
if (FAILED(hr)) if (FAILED(hr))
OnError(VideoCaptureError::kWinMediaFoundationGetMediaEventStatusFailed, OnError(VideoCaptureError::kWinMediaFoundationGetMediaEventStatusFailed,
...@@ -1350,4 +1376,35 @@ void VideoCaptureDeviceMFWin::SendOnStartedIfNotYetSent() { ...@@ -1350,4 +1376,35 @@ void VideoCaptureDeviceMFWin::SendOnStartedIfNotYetSent() {
client_->OnStarted(); client_->OnStarted();
} }
HRESULT VideoCaptureDeviceMFWin::WaitOnCaptureEvent(GUID capture_event_guid) {
HRESULT hr = S_OK;
HANDLE events[] = {nullptr, capture_error_.handle()};
// TODO(http://crbug.com/1093521): Add cases for Start
// MF_CAPTURE_ENGINE_PREVIEW_STARTED and MF_CAPTURE_ENGINE_PREVIEW_STOPPED
if (capture_event_guid == MF_CAPTURE_ENGINE_INITIALIZED) {
events[0] = capture_initialize_.handle();
} else {
// no registered event handle for the event requested
hr = E_NOTIMPL;
LogError(FROM_HERE, hr);
return hr;
}
DWORD wait_result =
::WaitForMultipleObjects(base::size(events), events, FALSE, INFINITE);
switch (wait_result) {
case WAIT_OBJECT_0:
break;
case WAIT_FAILED:
hr = HRESULT_FROM_WIN32(::GetLastError());
LogError(FROM_HERE, hr);
break;
default:
hr = E_UNEXPECTED;
LogError(FROM_HERE, hr);
break;
}
return hr;
}
} // namespace media } // namespace media
...@@ -117,6 +117,7 @@ class CAPTURE_EXPORT VideoCaptureDeviceMFWin : public VideoCaptureDevice { ...@@ -117,6 +117,7 @@ class CAPTURE_EXPORT VideoCaptureDeviceMFWin : public VideoCaptureDevice {
const base::Location& from_here, const base::Location& from_here,
const char* message); const char* message);
void SendOnStartedIfNotYetSent(); void SendOnStartedIfNotYetSent();
HRESULT WaitOnCaptureEvent(GUID capture_event_guid);
VideoFacingMode facing_mode_; VideoFacingMode facing_mode_;
CreateMFPhotoCallbackCB create_mf_photo_callback_; CreateMFPhotoCallbackCB create_mf_photo_callback_;
...@@ -145,6 +146,8 @@ class CAPTURE_EXPORT VideoCaptureDeviceMFWin : public VideoCaptureDevice { ...@@ -145,6 +146,8 @@ class CAPTURE_EXPORT VideoCaptureDeviceMFWin : public VideoCaptureDevice {
bool focus_mode_manual_; bool focus_mode_manual_;
bool white_balance_mode_manual_; bool white_balance_mode_manual_;
base::queue<TakePhotoCallback> video_stream_take_photo_callbacks_; base::queue<TakePhotoCallback> video_stream_take_photo_callbacks_;
base::WaitableEvent capture_initialize_;
base::WaitableEvent capture_error_;
SEQUENCE_CHECKER(sequence_checker_); SEQUENCE_CHECKER(sequence_checker_);
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include <wincodec.h> #include <wincodec.h>
#include "base/bind.h" #include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/test/task_environment.h"
#include "media/capture/video/win/sink_filter_win.h" #include "media/capture/video/win/sink_filter_win.h"
#include "media/capture/video/win/video_capture_device_factory_win.h" #include "media/capture/video/win/video_capture_device_factory_win.h"
#include "media/capture/video/win/video_capture_device_mf_win.h" #include "media/capture/video/win/video_capture_device_mf_win.h"
...@@ -413,11 +415,34 @@ class MockMFCaptureEngine ...@@ -413,11 +415,34 @@ class MockMFCaptureEngine
EXPECT_TRUE(pAttributes); EXPECT_TRUE(pAttributes);
EXPECT_TRUE(pVideoSource); EXPECT_TRUE(pVideoSource);
event_callback = pEventCallback; event_callback = pEventCallback;
OnCorrectInitialize(); OnCorrectInitializeQueued();
ON_CALL(*this, OnInitStatus).WillByDefault(Return(S_OK));
ON_CALL(*this, OnInitEventGuid)
.WillByDefault(Return(MF_CAPTURE_ENGINE_INITIALIZED));
// HW Cameras usually add about 500ms latency on init
ON_CALL(*this, InitEventDelay)
.WillByDefault(Return(base::TimeDelta::FromMilliseconds(500)));
base::TimeDelta event_delay = InitEventDelay();
base::ThreadPool::PostDelayedTask(
FROM_HERE,
base::BindOnce(&MockMFCaptureEngine::FireCaptureEvent, this,
OnInitEventGuid(), OnInitStatus()),
event_delay);
// if zero is passed ensure event fires before wait starts
if (event_delay == base::TimeDelta::FromMilliseconds(0)) {
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
}
return S_OK; return S_OK;
} }
MOCK_METHOD0(OnCorrectInitialize, void(void)); MOCK_METHOD0(OnCorrectInitializeQueued, void(void));
MOCK_METHOD0(OnInitEventGuid, GUID(void));
MOCK_METHOD0(OnInitStatus, HRESULT(void));
MOCK_METHOD0(InitEventDelay, base::TimeDelta(void));
IFACEMETHODIMP StartPreview(void) override { IFACEMETHODIMP StartPreview(void) override {
OnStartPreview(); OnStartPreview();
...@@ -456,8 +481,14 @@ class MockMFCaptureEngine ...@@ -456,8 +481,14 @@ class MockMFCaptureEngine
} }
MOCK_METHOD0(DoGetSource, IMFCaptureSource*()); MOCK_METHOD0(DoGetSource, IMFCaptureSource*());
void FireCaptureEvent(GUID event, HRESULT hrStatus) {
ComPtr<IMFMediaEvent> captureEvent;
MFCreateMediaEvent(MEExtendedType, event, hrStatus, nullptr, &captureEvent);
if (event_callback) {
event_callback->OnEvent(captureEvent.Get());
}
}
scoped_refptr<IMFCaptureEngineOnEventCallback> event_callback; scoped_refptr<IMFCaptureEngineOnEventCallback> event_callback;
private: private:
friend class base::RefCountedThreadSafe<MockMFCaptureEngine>; friend class base::RefCountedThreadSafe<MockMFCaptureEngine>;
virtual ~MockMFCaptureEngine() = default; virtual ~MockMFCaptureEngine() = default;
...@@ -872,7 +903,7 @@ class VideoCaptureDeviceMFWinTest : public ::testing::Test { ...@@ -872,7 +903,7 @@ class VideoCaptureDeviceMFWinTest : public ::testing::Test {
device_->set_max_retry_count_for_testing(3); device_->set_max_retry_count_for_testing(3);
device_->set_retry_delay_in_ms_for_testing(1); device_->set_retry_delay_in_ms_for_testing(1);
EXPECT_CALL(*(engine_.Get()), OnCorrectInitialize()); EXPECT_CALL(*(engine_.Get()), OnCorrectInitializeQueued());
EXPECT_TRUE(device_->Init()); EXPECT_TRUE(device_->Init());
EXPECT_CALL(*(engine_.Get()), DoGetSource()) EXPECT_CALL(*(engine_.Get()), DoGetSource())
.WillRepeatedly(Invoke([this]() { .WillRepeatedly(Invoke([this]() {
...@@ -1079,6 +1110,7 @@ class VideoCaptureDeviceMFWinTest : public ::testing::Test { ...@@ -1079,6 +1110,7 @@ class VideoCaptureDeviceMFWinTest : public ::testing::Test {
scoped_refptr<MockMFCaptureSource> capture_source_; scoped_refptr<MockMFCaptureSource> capture_source_;
scoped_refptr<MockCapturePreviewSink> capture_preview_sink_; scoped_refptr<MockCapturePreviewSink> capture_preview_sink_;
base::test::TaskEnvironment task_environment_;
private: private:
const bool media_foundation_supported_; const bool media_foundation_supported_;
...@@ -1118,6 +1150,60 @@ TEST_F(VideoCaptureDeviceMFWinTest, CallClientOnErrorMediaEvent) { ...@@ -1118,6 +1150,60 @@ TEST_F(VideoCaptureDeviceMFWinTest, CallClientOnErrorMediaEvent) {
engine_->event_callback->OnEvent(media_event_error.get()); engine_->event_callback->OnEvent(media_event_error.get());
} }
// Expects Init to fail due to OnError() event
TEST_F(VideoCaptureDeviceMFWinTest, CallClientOnErrorDurringInit) {
if (ShouldSkipTest())
return;
VideoCaptureDeviceDescriptor descriptor = VideoCaptureDeviceDescriptor();
Microsoft::WRL::ComPtr<MockMFMediaSource> media_source =
new MockMFMediaSource();
Microsoft::WRL::ComPtr<MockMFCaptureEngine> engine =
new MockMFCaptureEngine();
std::unique_ptr<VideoCaptureDeviceMFWin> device =
std::make_unique<VideoCaptureDeviceMFWin>(descriptor, media_source,
engine);
EXPECT_CALL(*(engine.Get()), OnInitEventGuid).WillOnce([]() {
return MF_CAPTURE_ENGINE_INITIALIZED;
});
// E_ACCESSDENIED is thrown if application is denied access in settings UI
EXPECT_CALL(*(engine.Get()), OnInitStatus).WillOnce([]() {
return E_ACCESSDENIED;
});
EXPECT_CALL(*(engine.Get()), OnCorrectInitializeQueued());
EXPECT_FALSE(device->Init());
}
// Expects Init to succeed but MF_CAPTURE_ENGINE_INITIALIZED fired before
// WaitOnCaptureEvent is called.
TEST_F(VideoCaptureDeviceMFWinTest, CallClientOnFireCaptureEngineInitEarly) {
if (ShouldSkipTest())
return;
VideoCaptureDeviceDescriptor descriptor = VideoCaptureDeviceDescriptor();
Microsoft::WRL::ComPtr<MockMFMediaSource> media_source =
new MockMFMediaSource();
Microsoft::WRL::ComPtr<MockMFCaptureEngine> engine =
new MockMFCaptureEngine();
std::unique_ptr<VideoCaptureDeviceMFWin> device =
std::make_unique<VideoCaptureDeviceMFWin>(descriptor, media_source,
engine);
EXPECT_CALL(*(engine.Get()), OnInitEventGuid).WillOnce([]() {
return MF_CAPTURE_ENGINE_INITIALIZED;
});
EXPECT_CALL(*(engine.Get()), InitEventDelay).WillOnce([]() {
return base::TimeDelta::FromMilliseconds(0);
});
EXPECT_CALL(*(engine.Get()), OnCorrectInitializeQueued());
EXPECT_TRUE(device->Init());
}
// Allocates device with flaky methods failing with MF_E_INVALIDREQUEST and // Allocates device with flaky methods failing with MF_E_INVALIDREQUEST and
// expects the device to retry and start correctly // expects the device to retry and start correctly
TEST_F(VideoCaptureDeviceMFWinTest, AllocateAndStartWithFlakyInvalidRequest) { TEST_F(VideoCaptureDeviceMFWinTest, AllocateAndStartWithFlakyInvalidRequest) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment