From 3f585782f161eb22d212c01bc9f5b303fc0ef5d4 Mon Sep 17 00:00:00 2001 From: shenweip <1037567878@qq.com> Date: Wed, 22 Jan 2020 21:13:50 +0800 Subject: [PATCH] Don't shut down camera when exiting games,also re-enumerating devices if received WM_DEVICECHANGE. --- Core/HLE/sceUsbCam.cpp | 1 - UI/NativeApp.cpp | 2 + Windows/CaptureDevice.cpp | 114 ++++++++++++++++++++++++++------------ Windows/CaptureDevice.h | 9 ++- Windows/MainWindow.cpp | 2 + 5 files changed, 90 insertions(+), 38 deletions(-) diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index da850ce8f9..6690c90d50 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -319,7 +319,6 @@ int Camera::stopCapture() { #elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP) if (winCamera) { winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr }); - winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr }); } #else ERROR_LOG(HLE, "%s not implemented", __FUNCTION__); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index ff4531beb4..32c98d75b7 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -893,6 +893,8 @@ void NativeShutdownGraphics() { #if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) if (winCamera) { + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr }); + while (!winCamera->isShutDown()) {};// Wait for shutting down. delete winCamera; winCamera = nullptr; } diff --git a/Windows/CaptureDevice.cpp b/Windows/CaptureDevice.cpp index 13883d6dfe..fa750e0bf5 100644 --- a/Windows/CaptureDevice.cpp +++ b/Windows/CaptureDevice.cpp @@ -24,6 +24,8 @@ #include "Core/HLE/sceUsbCam.h" #include "Core/Config.h" +bool isDeviceChanged = false; + namespace MFAPI { HINSTANCE Mflib; HINSTANCE Mfplatlib; @@ -355,6 +357,7 @@ WindowsCaptureDevice::WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type) : error(CAPTUREDEVIDE_ERROR_NO_ERROR), errorMessage(""), state(CAPTUREDEVIDE_STATE::UNINITIALIZED) { + param = { 0 }; switch (type) { case CAPTUREDEVIDE_TYPE::VIDEO: @@ -387,50 +390,26 @@ WindowsCaptureDevice::~WindowsCaptureDevice() { break; } } +void WindowsCaptureDevice::CheckDevices() { + isDeviceChanged = true; +} bool WindowsCaptureDevice::init() { HRESULT hr = S_OK; - param = { 0 }; - IMFAttributes *pAttributes = nullptr; if (!RegisterCMPTMFApis()) { setError(CAPTUREDEVIDE_ERROR_INIT_FAILED, "Cannot register devices"); return false; } - - hr = MFCreateAttributes(&pAttributes, 1); - if (SUCCEEDED(hr)) { - switch (type) { - case CAPTUREDEVIDE_TYPE::VIDEO: - hr = pAttributes->SetGUID( - MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, - MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID - ); - - break; - case CAPTUREDEVIDE_TYPE::AUDIO: - hr = pAttributes->SetGUID( - MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, - MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID - ); - - break; - default: - setError(CAPTUREDEVIDE_ERROR_UNKNOWN_TYPE, "Unknown device type"); - return false; - } - } - - if (SUCCEEDED(hr)) - hr = EnumDeviceSources(pAttributes, ¶m.ppDevices, ¶m.count); + std::unique_lock lock(paramMutex); + hr = enumDevices(); + lock.unlock(); if (FAILED(hr)) { setError(CAPTUREDEVIDE_ERROR_INIT_FAILED, "Cannot enumerate devices"); - SafeRelease(&pAttributes); return false; } - SafeRelease(&pAttributes); updateState(CAPTUREDEVIDE_STATE::STOPPED); return true; } @@ -441,7 +420,16 @@ bool WindowsCaptureDevice::start() { IMFMediaType *pType = nullptr; UINT32 selection = 0; UINT32 count = 0; - std::vector deviceList = getDeviceList(); + + // Release old sources first(if any). + SafeRelease(&m_pSource); + SafeRelease(&m_pReader); + if (m_pCallback) { + delete m_pCallback; + m_pCallback = nullptr; + } + // Need to re-enumerate the list,because old sources were released. + std::vector deviceList = getDeviceList(true); if (deviceList.size() < 1) { setError(CAPTUREDEVIDE_ERROR_START_FAILED, "Has no device"); @@ -511,7 +499,7 @@ bool WindowsCaptureDevice::start() { if (SUCCEEDED(hr)) hr = setDeviceParam(pType);*/ // Don't support on Win7 - // Request the first frame, in asnyc mode, OnReadSample will be called when ReadSample completed. + // Request the first frame, in async mode, OnReadSample will be called when ReadSample completed. if (SUCCEEDED(hr)) { hr = m_pReader->ReadSample( (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, @@ -572,7 +560,7 @@ bool WindowsCaptureDevice::stop() { return true; }; -std::vector WindowsCaptureDevice::getDeviceList(int *pActuallCount) { +std::vector WindowsCaptureDevice::getDeviceList(bool forceEnum, int *pActuallCount) { HRESULT hr = S_OK; UINT32 count = 0; LPWSTR pwstrName = nullptr; @@ -581,6 +569,23 @@ std::vector WindowsCaptureDevice::getDeviceList(int *pActuallCount) DWORD dwMinSize = 0; std::vector deviceList; + if (isDeviceChanged || forceEnum) { + std::unique_lock lock(paramMutex); + for (DWORD i = 0; i < param.count; i++) { + SafeRelease(¶m.ppDevices[i]); + } + CoTaskMemFree(param.ppDevices); // Null pointer is okay. + + hr = enumDevices(); + + lock.unlock(); + + if (SUCCEEDED(hr)) + isDeviceChanged = false; + else + return deviceList; + } + for (; count < param.count; count++) { hr = param.ppDevices[count]->GetAllocatedString( MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, @@ -722,17 +727,54 @@ void WindowsCaptureDevice::messageHandler() { std::lock_guard lock(sdMutex); SafeRelease(&m_pSource); SafeRelease(&m_pReader); - CoTaskMemFree(param.ppDevices); delete m_pCallback; unRegisterCMPTMFApis(); + std::unique_lock lock2(paramMutex); + for (DWORD i = 0; i < param.count; i++) { + SafeRelease(¶m.ppDevices[i]); + } + CoTaskMemFree(param.ppDevices); // Null pointer is okay. + lock2.unlock(); + MFShutdown(); CoUninitialize(); updateState(CAPTUREDEVIDE_STATE::SHUTDOWN); +} - // Make sure we don't try to loop through the devices later in getDeviceList()... - param = {}; +HRESULT WindowsCaptureDevice::enumDevices() { + HRESULT hr = S_OK; + IMFAttributes *pAttributes = nullptr; + + hr = MFCreateAttributes(&pAttributes, 1); + if (SUCCEEDED(hr)) { + switch (type) { + case CAPTUREDEVIDE_TYPE::VIDEO: + hr = pAttributes->SetGUID( + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID + ); + + break; + case CAPTUREDEVIDE_TYPE::AUDIO: + hr = pAttributes->SetGUID( + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID + ); + + break; + default: + setError(CAPTUREDEVIDE_ERROR_UNKNOWN_TYPE, "Unknown device type"); + return E_FAIL; + } + } + if (SUCCEEDED(hr)) { + hr = EnumDeviceSources(pAttributes, ¶m.ppDevices, ¶m.count); + } + + SafeRelease(&pAttributes); + return hr; } //----------------------------------------------------------------------------- diff --git a/Windows/CaptureDevice.h b/Windows/CaptureDevice.h index ea76a971b5..06bb86c376 100644 --- a/Windows/CaptureDevice.h +++ b/Windows/CaptureDevice.h @@ -160,6 +160,8 @@ public: WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type); ~WindowsCaptureDevice(); + static void CheckDevices(); + bool init(); bool start(); bool stop(); @@ -168,7 +170,7 @@ public: std::string getErrorMessage() const { return errorMessage; } int getDeviceCounts() const { return param.count; } // Get a list contained friendly device name. - std::vector getDeviceList(int *pActuallCount = nullptr); + std::vector getDeviceList(bool forceEnum = false, int *pActuallCount = nullptr); void setError(const CAPTUREDEVIDE_ERROR &newError, const std::string &newErrorMessage) { error = newError; errorMessage = newErrorMessage; } void setSelction(const UINT32 &selection) { param.selection = selection; } @@ -180,6 +182,8 @@ public: void sendMessage(CAPTUREDEVIDE_MESSAGE message); CAPTUREDEVIDE_MESSAGE getMessage(); + HRESULT enumDevices(); + friend class ReaderCallback; protected: @@ -208,6 +212,9 @@ protected: // For the shutdown event safety. std::mutex sdMutex; +// Param updating synchronously. + std::mutex paramMutex; + // Camera only unsigned char *imageRGB; int imgRGBLineSizes[4]; diff --git a/Windows/MainWindow.cpp b/Windows/MainWindow.cpp index fe66c287c8..2462568dba 100644 --- a/Windows/MainWindow.cpp +++ b/Windows/MainWindow.cpp @@ -72,6 +72,7 @@ #include "Windows/W32Util/ShellUtil.h" #include "Windows/W32Util/Misc.h" #include "Windows/RawInput.h" +#include "Windows/CaptureDevice.h" #include "Windows/TouchInputHandler.h" #include "Windows/MainWindowMenu.h" #include "GPU/GPUInterface.h" @@ -848,6 +849,7 @@ namespace MainWindow #ifndef _M_ARM DinputDevice::CheckDevices(); #endif + WindowsCaptureDevice::CheckDevices(); return DefWindowProc(hWnd, message, wParam, lParam); case WM_VERYSLEEPY_MSG: