diff --git a/CMakeLists.txt b/CMakeLists.txt index 5eab3c7033eb..df14ac85a6ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1864,6 +1864,9 @@ set(WindowsFiles Windows/GEDebugger/GEDebugger.h Windows/GEDebugger/TabDisplayLists.h Windows/GEDebugger/TabVertices.h + Windows/BufferLock.h + Windows/CaptureDevice.cpp + Windows/CaptureDevice.h Windows/DinputDevice.cpp Windows/DinputDevice.h Windows/DSoundStream.cpp @@ -1917,7 +1920,7 @@ set(WindowsFiles list(APPEND LinkCommon ${CoreLibName} ${CMAKE_THREAD_LIBS_INIT}) if(WIN32) - list(APPEND LinkCommon kernel32 user32 gdi32 shell32 comctl32 dsound xinput d3d9 winmm dinput8 ole32 winspool ksuser) + list(APPEND LinkCommon kernel32 user32 gdi32 shell32 comctl32 dsound xinput d3d9 winmm dinput8 ole32 winspool ksuser mf mfplat mfreadwrite mfuuid shlwapi) #setup_target_project(${TargetBin} Windows) list(APPEND NativeAppSource ${WindowsFiles}) endif() diff --git a/Common/OSVersion.cpp b/Common/OSVersion.cpp index f66c44136524..adc2f1d310a8 100644 --- a/Common/OSVersion.cpp +++ b/Common/OSVersion.cpp @@ -95,6 +95,25 @@ bool IsVistaOrHigher() { #endif } +bool IsWin7OrHigher() { +#if PPSSPP_PLATFORM(UWP) + return true; +#else + OSVERSIONINFOEX osvi; + DWORDLONG dwlConditionMask = 0; + int op = VER_GREATER_EQUAL; + ZeroMemory(&osvi, sizeof(osvi)); + osvi.dwOSVersionInfoSize = sizeof(osvi); + osvi.dwMajorVersion = 6; // Win7 is 6.1 + osvi.dwMinorVersion = 1; + + VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, op); + VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, op); + + return VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION, dwlConditionMask) != FALSE; +#endif +} + std::string GetWindowsVersion() { const bool IsWindowsXPSP2 = DoesVersionMatchWindows(5, 1, 2, 0, false); const bool IsWindowsXPSP3 = DoesVersionMatchWindows(5, 1, 3, 0, false); diff --git a/Common/OSVersion.h b/Common/OSVersion.h index 1f441e412cf4..262cba05ac38 100644 --- a/Common/OSVersion.h +++ b/Common/OSVersion.h @@ -5,6 +5,7 @@ #ifdef _WIN32 bool IsVistaOrHigher(); +bool IsWin7OrHigher(); bool DoesVersionMatchWindows(uint32_t major, uint32_t minor, uint32_t spMajor, uint32_t spMinor, bool acceptGreater); std::string GetWindowsVersion(); std::string GetWindowsSystemArchitecture(); diff --git a/Core/Config.cpp b/Core/Config.cpp index 1fc75131f045..3dd37d49c764 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -706,6 +706,9 @@ static ConfigSetting graphicsSettings[] = { ConfigSetting("VulkanDevice", &g_Config.sVulkanDevice, "", true, false), #ifdef _WIN32 ConfigSetting("D3D11Device", &g_Config.sD3D11Device, "", true, false), +#endif +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + ConfigSetting("WinCameraDevice", &g_Config.sWinCameraDevice, "", true, false), #endif ConfigSetting("VendorBugChecksEnabled", &g_Config.bVendorBugChecksEnabled, true, false, false), ReportedConfigSetting("RenderingMode", &g_Config.iRenderingMode, 1, true, true), diff --git a/Core/Config.h b/Core/Config.h index 2fe185fcd7c1..3252164101bc 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -128,6 +128,7 @@ struct Config { // If not set, will use the "best" device. std::string sVulkanDevice; std::string sD3D11Device; // Windows only + std::string sWinCameraDevice; // Windows only bool bSoftwareRendering; bool bHardwareTransform; // only used in the GLES backend diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index 7667cc335fd6..50f1147b05dd 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -25,6 +25,12 @@ #include "Core/HLE/sceUsbCam.h" #include "Core/MemMapHelpers.h" +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) +#include "Windows/CaptureDevice.h" +#undef min +#endif + + PspUsbCamSetupMicParam micParam; PspUsbCamSetupVideoParam videoParam; @@ -32,6 +38,7 @@ unsigned int videoBufferLength = 0; unsigned int nextVideoFrame = 0; uint8_t *videoBuffer; std::mutex videoBufferMutex; +bool isShutDown = false; enum { VIDEO_BUFFER_SIZE = 40 * 1000, @@ -39,9 +46,17 @@ enum { void __UsbCamInit() { videoBuffer = new uint8_t[VIDEO_BUFFER_SIZE]; + isShutDown = false; } void __UsbCamShutdown() { +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + if (winCamera) { + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr }); + } +#endif + isShutDown = true; + delete[] videoBuffer; videoBuffer = nullptr; } @@ -97,13 +112,31 @@ static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) { static int sceUsbCamStartVideo() { INFO_LOG(HLE, "UNIMPL sceUsbCamStartVideo"); +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + if (winCamera) { + if (winCamera->isShutDown()) { + delete winCamera; + winCamera = new WindowsCaptureDevice(CAPTUREDEVIDE_TYPE::VIDEO); + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::INITIALIZE, nullptr }); + } + + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::START, nullptr }); + } + +#else System_SendMessage("camera_command", "startVideo"); +#endif return 0; } static int sceUsbCamStopVideo() { INFO_LOG(HLE, "UNIMPL sceUsbCamStopVideo"); +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + if (winCamera) + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr }); +#else System_SendMessage("camera_command", "stopVideo"); +#endif return 0; } @@ -114,7 +147,6 @@ static int sceUsbCamAutoImageReverseSW(int rev) { static int sceUsbCamReadVideoFrameBlocking(u32 bufAddr, u32 size) { std::lock_guard lock(videoBufferMutex); - u32 transferSize = std::min(videoBufferLength, size); if (Memory::IsValidRange(bufAddr, size)) { Memory::Memcpy(bufAddr, videoBuffer, transferSize); @@ -203,6 +235,8 @@ void Register_sceUsbCam() void Camera::pushCameraImage(long long length, unsigned char* image) { std::lock_guard lock(videoBufferMutex); + if (isShutDown) + return; memset(videoBuffer, 0, VIDEO_BUFFER_SIZE); if (length > VIDEO_BUFFER_SIZE) { videoBufferLength = 0; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index ae317198d6dd..ab5975c65eec 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -68,6 +68,7 @@ #include "Windows/MainWindow.h" #include #include "Windows/W32Util/ShellUtil.h" +#include "Windows/CaptureDevice.h" #endif GameSettingsScreen::GameSettingsScreen(std::string gamePath, std::string gameID, bool editThenRestore) @@ -262,6 +263,14 @@ void GameSettingsScreen::CreateViews() { softwareGPU->SetEnabled(false); } +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + if (winCamera && winCamera->getDeviceList().size() >= 1) { + graphicsSettings->Add(new ItemHeader(gr->T("Camera"))); + PopupMultiChoiceDynamic *cameraChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sWinCameraDevice, gr->T("Camera Device"), winCamera->getDeviceList(), nullptr, screenManager())); + } +#endif + + graphicsSettings->Add(new ItemHeader(gr->T("Frame Rate Control"))); static const char *frameSkip[] = {"Off", "1", "2", "3", "4", "5", "6", "7", "8"}; graphicsSettings->Add(new PopupMultiChoice(&g_Config.iFrameSkip, gr->T("Frame Skipping"), frameSkip, 0, ARRAY_SIZE(frameSkip), gr->GetName(), screenManager())); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index c04ecd7bc445..ff4531beb4c1 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -39,6 +39,10 @@ #include "Windows/MainWindow.h" #endif +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) +#include "Windows/CaptureDevice.h" +#endif + #include "base/display.h" #include "base/timeutil.h" #include "base/logging.h" @@ -67,6 +71,7 @@ #include "Common/LogManager.h" #include "Common/MemArena.h" #include "Common/GraphicsContext.h" +#include "Common/OSVersion.h" #include "Core/Config.h" #include "Core/ConfigValues.h" #include "Core/Core.h" @@ -855,6 +860,13 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) { #endif #endif +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + if (IsWin7OrHigher()) { + winCamera = new WindowsCaptureDevice(CAPTUREDEVIDE_TYPE::VIDEO); + winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::INITIALIZE, nullptr }); + } +#endif + g_gameInfoCache = new GameInfoCache(); if (gpu) @@ -879,6 +891,13 @@ void NativeShutdownGraphics() { winAudioBackend = nullptr; #endif +#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) + if (winCamera) { + delete winCamera; + winCamera = nullptr; + } +#endif + ShutdownWebServer(); UIBackgroundShutdown(); diff --git a/UI/UI.vcxproj b/UI/UI.vcxproj index 769c1272617a..ea06201cf550 100644 --- a/UI/UI.vcxproj +++ b/UI/UI.vcxproj @@ -207,7 +207,7 @@ Level3 _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;_ARCH_32=1;WIN32;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext true false false @@ -231,7 +231,7 @@ Level3 _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;_ARCH_64=1;WIN32;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext true false false @@ -281,7 +281,7 @@ Level3 _CRTDBG_MAP_ALLOC;USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;_ARCH_32=1;WIN32;_DEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext true false false @@ -310,7 +310,7 @@ true true USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;_ARCH_32=1;WIN32;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext false Size true @@ -339,7 +339,7 @@ true true USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;_ARCH_64=1;WIN32;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext false true false @@ -401,7 +401,7 @@ true true USING_WIN_UI;_CRT_SECURE_NO_WARNINGS;_ARCH_32=1;WIN32;NDEBUG;_LIB;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../common;..;../ext/native;../ext/glew;../ext/snappy;../ext/zlib;../ext/native/ext false true false diff --git a/Windows/BufferLock.h b/Windows/BufferLock.h new file mode 100644 index 000000000000..a19b8c965d13 --- /dev/null +++ b/Windows/BufferLock.h @@ -0,0 +1,123 @@ +////////////////////////////////////////////////////////////////////////// +// +// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF +// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO +// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A +// PARTICULAR PURPOSE. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// +////////////////////////////////////////////////////////////////////////// + + + +#pragma once + + +//------------------------------------------------------------------- +// VideoBufferLock class +// +// Locks a video buffer that might or might not support IMF2DBuffer. +// +//------------------------------------------------------------------- + +class VideoBufferLock +{ +public: + VideoBufferLock(IMFMediaBuffer *pBuffer) : m_p2DBuffer(NULL), m_bLocked(FALSE) + { + m_pBuffer = pBuffer; + m_pBuffer->AddRef(); + + // Query for the 2-D buffer interface. OK if this fails. + (void)m_pBuffer->QueryInterface(IID_PPV_ARGS(&m_p2DBuffer)); + } + + ~VideoBufferLock() + { + UnlockBuffer(); + SafeRelease(&m_pBuffer); + SafeRelease(&m_p2DBuffer); + } + + //------------------------------------------------------------------- + // LockBuffer + // + // Locks the buffer. Returns a pointer to scan line 0 and returns the stride. + // + // The caller must provide the default stride as an input parameter, in case + // the buffer does not expose IMF2DBuffer. You can calculate the default stride + // from the media type. + //------------------------------------------------------------------- + + HRESULT LockBuffer( + LONG lDefaultStride, // Minimum stride (with no padding). + DWORD dwHeightInPixels, // Height of the image, in pixels. + BYTE **ppbScanLine0, // Receives a pointer to the start of scan line 0. + LONG *plStride // Receives the actual stride. + ) + { + HRESULT hr = S_OK; + + // Use the 2-D version if available. + if (m_p2DBuffer) + { + hr = m_p2DBuffer->Lock2D(ppbScanLine0, plStride); + } + else + { + // Use non-2D version. + BYTE *pData = NULL; + + hr = m_pBuffer->Lock(&pData, NULL, NULL); + if (SUCCEEDED(hr)) + { + *plStride = lDefaultStride; + if (lDefaultStride < 0) + { + // Bottom-up orientation. Return a pointer to the start of the + // last row *in memory* which is the top row of the image. + *ppbScanLine0 = pData + abs(lDefaultStride) * (dwHeightInPixels - 1); + } + else + { + // Top-down orientation. Return a pointer to the start of the + // buffer. + *ppbScanLine0 = pData; + } + } + } + + m_bLocked = (SUCCEEDED(hr)); + + return hr; + } + + //------------------------------------------------------------------- + // UnlockBuffer + // + // Unlocks the buffer. Called automatically by the destructor. + //------------------------------------------------------------------- + + void UnlockBuffer() + { + if (m_bLocked) + { + if (m_p2DBuffer) + { + (void)m_p2DBuffer->Unlock2D(); + } + else + { + (void)m_pBuffer->Unlock(); + } + m_bLocked = FALSE; + } + } + +private: + IMFMediaBuffer *m_pBuffer; + IMF2DBuffer *m_p2DBuffer; + + BOOL m_bLocked; +}; diff --git a/Windows/CaptureDevice.cpp b/Windows/CaptureDevice.cpp new file mode 100644 index 000000000000..ef0907f19ec9 --- /dev/null +++ b/Windows/CaptureDevice.cpp @@ -0,0 +1,779 @@ +// Copyright (c) 2020- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include +#include "thread/threadutil.h" +#include "CaptureDevice.h" +#include "BufferLock.h" +#include "ext/jpge/jpge.h" +#include "CommonTypes.h" +#include "Core/HLE/sceUsbCam.h" +#include "Core/Config.h" + +namespace MFAPI { + HINSTANCE Mflib; + HINSTANCE Mfplatlib; + HINSTANCE Mfreadwritelib; + + typedef HRESULT(WINAPI *MFEnumDeviceSourcesFunc)(IMFAttributes *, IMFActivate ***, UINT32 *); + typedef HRESULT(WINAPI *MFGetStrideForBitmapInfoHeaderFunc)(DWORD, DWORD, LONG *); + typedef HRESULT(WINAPI *MFCreateSourceReaderFromMediaSourceFunc)(IMFMediaSource *, IMFAttributes *, IMFSourceReader **); + typedef HRESULT(WINAPI *MFCopyImageFunc)(BYTE *, LONG, const BYTE *, LONG, DWORD, DWORD); + + MFEnumDeviceSourcesFunc EnumDeviceSources; + MFGetStrideForBitmapInfoHeaderFunc GetStrideForBitmapInfoHeader; + MFCreateSourceReaderFromMediaSourceFunc CreateSourceReaderFromMediaSource; + MFCopyImageFunc CopyImage; +} + +using namespace MFAPI; + +bool RegisterCMPTMFApis(){ + //For the compatibility,these funcs don't be supported on vista. + Mflib = LoadLibrary(L"Mf.dll"); + Mfplatlib = LoadLibrary(L"Mfplat.dll"); + Mfreadwritelib = LoadLibrary(L"Mfreadwrite.dll"); + if (!Mflib || !Mfplatlib || !Mfreadwritelib) + return false; + + EnumDeviceSources = (MFEnumDeviceSourcesFunc)GetProcAddress(Mflib, "MFEnumDeviceSources"); + GetStrideForBitmapInfoHeader = (MFGetStrideForBitmapInfoHeaderFunc)GetProcAddress(Mfplatlib, "MFGetStrideForBitmapInfoHeader"); + MFAPI::CopyImage = (MFCopyImageFunc)GetProcAddress(Mfplatlib, "MFCopyImage"); + CreateSourceReaderFromMediaSource = (MFCreateSourceReaderFromMediaSourceFunc)GetProcAddress(Mfreadwritelib, "MFCreateSourceReaderFromMediaSource"); + if (!EnumDeviceSources || !GetStrideForBitmapInfoHeader || !CreateSourceReaderFromMediaSource || !MFAPI::CopyImage) + return false; + + return true; +} + +bool unRegisterCMPTMFApis() { + if (Mflib) { + FreeLibrary(Mflib); + Mflib = nullptr; + } + + if (Mfplatlib) { + FreeLibrary(Mfplatlib); + Mfplatlib = nullptr; + } + + if (Mfreadwritelib) { + FreeLibrary(Mfreadwritelib); + Mfreadwritelib = nullptr; + } + + EnumDeviceSources = nullptr; + GetStrideForBitmapInfoHeader = nullptr; + CreateSourceReaderFromMediaSource = nullptr; + MFAPI::CopyImage = nullptr; + + return true; +} + +WindowsCaptureDevice *winCamera; + +// TODO: Add more formats, but need some tests. +VideoFormatTransform g_VideoFormats[] = +{ + { MFVideoFormat_RGB32, AV_PIX_FMT_RGBA }, + { MFVideoFormat_RGB24, AV_PIX_FMT_RGB24 }, + { MFVideoFormat_YUY2, AV_PIX_FMT_YUYV422 }, + { MFVideoFormat_NV12, AV_PIX_FMT_NV12 } +}; + +const int g_cVideoFormats = 4; + + +MediaParam defaultVideoParam = { 480, 272, 0, MFVideoFormat_RGB24 }; +MediaParam defaultAudioParam = { 44100, 2, 0, MFAudioFormat_PCM }; + +HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride); + +ReaderCallback::ReaderCallback(WindowsCaptureDevice *device): img_convert_ctx(nullptr){ + this->device = device; +} + +ReaderCallback::~ReaderCallback() { + sws_freeContext(img_convert_ctx); +} + +HRESULT ReaderCallback::QueryInterface(REFIID riid, void** ppv) +{ + static const QITAB qit[] = + { + QITABENT(ReaderCallback, IMFSourceReaderCallback), + { 0 }, + }; + return QISearch(this, qit, riid, ppv); +} + +HRESULT ReaderCallback::OnReadSample( + HRESULT hrStatus, + DWORD dwStreamIndex, + DWORD dwStreamFlags, + LONGLONG llTimestamp, + IMFSample *pSample) { + HRESULT hr = S_OK; + IMFMediaBuffer *pBuffer = nullptr; + LONG lStride = 0; + std::lock_guard lock(device->sdMutex); + if (device->isShutDown()) + return hr; + + if (FAILED(hrStatus)) + hr = hrStatus; + + if (SUCCEEDED(hr)) { + if (pSample) { + hr = pSample->GetBufferByIndex(0, &pBuffer); + } + } + if (SUCCEEDED(hr)) { + switch (device->type) { + case CAPTUREDEVIDE_TYPE::VIDEO: { + BYTE *pbScanline0 = nullptr; + VideoBufferLock *videoBuffer = nullptr; + int imgJpegSize = device->imgJpegSize; + unsigned char* invertedSrcImg = nullptr; + LONG srcPadding = 0; + + UINT32 srcW = device->deviceParam.width; + UINT32 srcH = device->deviceParam.height; + UINT32 dstW = device->targetMediaParam.width; + UINT32 dstH = device->targetMediaParam.height; + GUID srcMFVideoFormat = device->deviceParam.videoFormat; + + // pSample can be null, in this case ReadSample still should be called to request next frame. + if (pSample) { + videoBuffer = new VideoBufferLock(pBuffer); + hr = videoBuffer->LockBuffer(device->deviceParam.default_stride, device->deviceParam.height, &pbScanline0, &lStride); + + if (lStride > 0) + srcPadding = lStride - device->deviceParam.default_stride; + else + srcPadding = device->deviceParam.default_stride - lStride; + + if (SUCCEEDED(hr)) { + // Convert image to RGB24 + if (lStride > 0) { + imgConvert( + device->imageRGB, dstW, dstH, device->imgRGBLineSizes, + pbScanline0, srcW, srcH, srcMFVideoFormat, srcPadding); + } else { + // If stride < 0, the pointer to the first row of source image is the last row in memory,should invert it in memory. + invertedSrcImg = (unsigned char*)av_malloc(av_image_get_buffer_size(getAVVideoFormatbyMFVideoFormat(srcMFVideoFormat), srcW, srcH, 1)); + imgInvert(invertedSrcImg, pbScanline0, srcW, srcH, device->deviceParam.videoFormat, lStride); + // We alloc a inverted image with no padding, set padding to zero. + srcPadding = 0; + imgConvert( + device->imageRGB, dstW, dstH, device->imgRGBLineSizes, + invertedSrcImg, srcW, srcH, srcMFVideoFormat, srcPadding); + av_free(invertedSrcImg); + } + + // Compress image to jpeg from RGB24. + jpge::compress_image_to_jpeg_file_in_memory( + device->imageJpeg, imgJpegSize, + dstW, + dstH, + 3, + device->imageRGB); + } + Camera::pushCameraImage(imgJpegSize, device->imageJpeg); + } + // Request the next frame. + if (SUCCEEDED(hr)) { + hr = device->m_pReader->ReadSample( + (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, + 0, + nullptr, + nullptr, + nullptr, + nullptr + ); + } + + delete videoBuffer; + break; + } + case CAPTUREDEVIDE_TYPE::AUDIO: + // TODO: + break; + } + } + + SafeRelease(&pBuffer); + return hr; +} + +AVPixelFormat ReaderCallback::getAVVideoFormatbyMFVideoFormat(const GUID &MFVideoFormat) { + for (int i = 0; i < g_cVideoFormats; i++) { + if (MFVideoFormat == g_VideoFormats[i].MFVideoFormat) + return g_VideoFormats[i].AVVideoFormat; + } + return AV_PIX_FMT_RGB24; +} + +void ReaderCallback::imgConvert( + unsigned char *dst, unsigned int &dstW, unsigned int &dstH, int dstLineSizes[4], + unsigned char *src, const unsigned int &srcW, const unsigned int &srcH, const GUID &srcFormat, + const int &srcPadding) { + int srcLineSizes[4] = { 0, 0, 0, 0 }; + unsigned char *pSrc[4]; + unsigned char *pDst[4]; + + AVPixelFormat srcAvFormat = getAVVideoFormatbyMFVideoFormat(srcFormat); + + av_image_fill_linesizes(srcLineSizes, srcAvFormat, srcW); + + // Is this correct? + if (srcPadding != 0) { + for (int i = 0; i < 4; i++) { + if (srcLineSizes[i] != 0) + srcLineSizes[i] += srcPadding; + } + } + + av_image_fill_pointers(pSrc, srcAvFormat, srcH, src, srcLineSizes); + av_image_fill_pointers(pDst, AV_PIX_FMT_RGB24, dstH, dst, dstLineSizes); + + + + if (img_convert_ctx == nullptr) { + img_convert_ctx = sws_getContext( + srcW, + srcH, + srcAvFormat, + dstW, + dstH, + AV_PIX_FMT_RGB24, + SWS_BICUBIC, + nullptr, + nullptr, + nullptr + ); + } + + if (img_convert_ctx) { + sws_scale(img_convert_ctx, + (const uint8_t *const *)pSrc, + srcLineSizes, + 0, + srcH, + (uint8_t *const *)pDst, + dstLineSizes + ); + } +} + +void ReaderCallback::imgInvert(unsigned char *dst, unsigned char *src, const int &srcW, const int &srcH, const GUID &srcFormat, const int &srcStride) { + AVPixelFormat srcAvFormat = getAVVideoFormatbyMFVideoFormat(srcFormat); + int dstLineSizes[4] = { 0, 0, 0, 0 }; + + av_image_fill_linesizes(dstLineSizes, srcAvFormat, srcW); + + if(srcFormat == MFVideoFormat_RGB32) + imgInvertRGBA(dst, dstLineSizes[0], src, srcStride, srcH); + else if(srcFormat == MFVideoFormat_RGB24) + imgInvertRGB(dst, dstLineSizes[0], src, srcStride, srcH); + else if (srcFormat == MFVideoFormat_YUY2) + imgInvertYUY2(dst, dstLineSizes[0], src, srcStride, srcH); + else if (srcFormat == MFVideoFormat_NV12) + imgInvertNV12(dst, dstLineSizes[0], src, srcStride, srcH);; +} + +void ReaderCallback::imgInvertRGBA(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h) { + MFAPI::CopyImage(dst, dstStride, src, srcStride, dstStride, h); +} + +void ReaderCallback::imgInvertRGB(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h) { + for (int y = 0; y < h; y++) { + for (int srcx = dstStride - 1, dstx = 0; dstx < dstStride; srcx--, dstx++) { + dst[dstx] = src[srcx]; + } + dst += dstStride; + src += srcStride; + } +} + +void ReaderCallback::imgInvertYUY2(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h) { + for (int y = 0; y < h; y++) { + for (int srcx = dstStride - 1, dstx = 0; dstx < dstStride; srcx--, dstx++) { + dst[dstx] = src[srcx]; + } + dst += dstStride; + src += srcStride; + } +} + +void ReaderCallback::imgInvertNV12(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h) { + unsigned char *dstY = dst; + unsigned char *dstU = dst + dstStride * h; + unsigned char *srcY = src; + unsigned char *srcV = src + srcStride * h; + + unsigned char *srcY1 = srcY; + unsigned char *srcY2 = srcY1 + srcStride; + unsigned char *dstY1 = dstY; + unsigned char *dstY2 = dstY1 + dstStride; + + bool isodd = h % 2 != 0; + + for (int y = 0; y < (isodd ? h - 1 : h); y += 2) { + for (int srcx = dstStride - 1, dstx = 0; dstx < dstStride; srcx--, dstx++) { + dstY1[dstx] = srcY1[srcx]; + dstY2[dstx] = srcY2[srcx]; + dstU[dstx] = srcV[srcx]; + } + dstY += dstStride * 2; + srcY1 += srcStride * 2; + srcY2 += srcStride *2; + srcV += srcStride; + dstU += dstStride; + } +} + +WindowsCaptureDevice::WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type) : + type(type), + m_pCallback(nullptr), + m_pSource(nullptr), + m_pReader(nullptr), + error(CAPTUREDEVIDE_ERROR_NO_ERROR), + errorMessage(""), + state(CAPTUREDEVIDE_STATE::UNINITIALIZED) { + + switch (type) { + case CAPTUREDEVIDE_TYPE::VIDEO: + targetMediaParam = defaultVideoParam; + imageRGB = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, targetMediaParam.width, targetMediaParam.height, 1)); + av_image_fill_linesizes(imgRGBLineSizes, AV_PIX_FMT_RGB24, targetMediaParam.width); + imgJpegSize = av_image_get_buffer_size(AV_PIX_FMT_YUVJ411P, targetMediaParam.width, targetMediaParam.height, 1); + imageJpeg = (unsigned char*)av_malloc(imgJpegSize); + break; + case CAPTUREDEVIDE_TYPE::AUDIO: + // TODO: + targetMediaParam = defaultAudioParam; + imageRGB = nullptr; + imageJpeg = nullptr; + break; + } + + std::thread t(&WindowsCaptureDevice::messageHandler, this); + t.detach(); +} + +WindowsCaptureDevice::~WindowsCaptureDevice() { + switch (type) { + case CAPTUREDEVIDE_TYPE::VIDEO: + av_free(imageRGB); + av_free(imageJpeg); + break; + case CAPTUREDEVIDE_TYPE::AUDIO: + // TODO: + break; + } +} + +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); + + if (FAILED(hr)) { + setError(CAPTUREDEVIDE_ERROR_INIT_FAILED, "Cannot enumerate devices"); + SafeRelease(&pAttributes); + return false; + } + + SafeRelease(&pAttributes); + updateState(CAPTUREDEVIDE_STATE::STOPPED); + return true; +} + +bool WindowsCaptureDevice::start() { + HRESULT hr = S_OK; + IMFAttributes *pAttributes = nullptr; + IMFMediaType *pType = nullptr; + UINT32 selection = 0; + UINT32 count = 0; + std::vector deviceList = getDeviceList(); + + if (deviceList.size() < 1) { + setError(CAPTUREDEVIDE_ERROR_START_FAILED, "Has no device"); + return false; + } + + m_pCallback = new ReaderCallback(this); + + switch (state) { + case CAPTUREDEVIDE_STATE::STOPPED: + for (auto &name : deviceList) { + if (name == g_Config.sWinCameraDevice) { + selection = count; + break; + } + ++count; + } + setSelction(selection); + hr = param.ppDevices[param.selection]->ActivateObject( + __uuidof(IMFMediaSource), + (void**)&m_pSource); + + if (SUCCEEDED(hr)) + hr = MFCreateAttributes(&pAttributes, 2); + + // Use async mode + if (SUCCEEDED(hr)) + hr = pAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, m_pCallback); + + if (SUCCEEDED(hr)) + hr = pAttributes->SetUINT32(MF_READWRITE_DISABLE_CONVERTERS, TRUE); + + if (SUCCEEDED(hr)) { + hr = CreateSourceReaderFromMediaSource( + m_pSource, + pAttributes, + &m_pReader + ); + } + + if (!m_pReader) + hr = -1; + + if (SUCCEEDED(hr)) { + switch (type) { + case CAPTUREDEVIDE_TYPE::VIDEO: + for (DWORD i = 0; ; i++) { + hr = m_pReader->GetNativeMediaType( + (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, + i, + &pType + ); + + if (FAILED(hr)) { break; } + + hr = setDeviceParam(pType); + + if (SUCCEEDED(hr)) + break; + } + /* + hr = m_pReader->GetNativeMediaType( + (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, + (DWORD)0xFFFFFFFF,//MF_SOURCE_READER_CURRENT_TYPE_INDEX + &pType + ); + 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. + if (SUCCEEDED(hr)) { + hr = m_pReader->ReadSample( + (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, + 0, + nullptr, + nullptr, + nullptr, + nullptr + ); + } + + break; + + case CAPTUREDEVIDE_TYPE::AUDIO: + // TODO: + break; + } + } + + if (FAILED(hr)) { + setError(CAPTUREDEVIDE_ERROR_START_FAILED, "Cannot start"); + if(m_pSource) + m_pSource->Shutdown(); + SafeRelease(&m_pSource); + SafeRelease(&pAttributes); + SafeRelease(&pType); + SafeRelease(&m_pReader); + return false; + } + + SafeRelease(&pAttributes); + SafeRelease(&pType); + updateState(CAPTUREDEVIDE_STATE::STARTED); + break; + case CAPTUREDEVIDE_STATE::LOST: + setError(CAPTUREDEVIDE_ERROR_START_FAILED, "Device has lost"); + return false; + case CAPTUREDEVIDE_STATE::STARTED: + setError(CAPTUREDEVIDE_ERROR_START_FAILED, "Device has started"); + return false; + case CAPTUREDEVIDE_STATE::UNINITIALIZED: + setError(CAPTUREDEVIDE_ERROR_START_FAILED, "Device doesn't initialize"); + return false; + default: + break; + } + return true; +} + +bool WindowsCaptureDevice::stop() { + if (state == CAPTUREDEVIDE_STATE::STOPPED) + return true; + if (m_pSource) + m_pSource->Stop(); + + updateState(CAPTUREDEVIDE_STATE::STOPPED); + + return true; +}; + +std::vector WindowsCaptureDevice::getDeviceList(int *pActuallCount) { + HRESULT hr = S_OK; + UINT32 count = 0; + LPWSTR pwstrName = nullptr; + char *cstrName = nullptr; + std::string strName; + DWORD dwMinSize = 0; + std::vector deviceList; + + for (; count < param.count; count++) { + hr = param.ppDevices[count]->GetAllocatedString( + MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, + &pwstrName, + nullptr + ); + + if (SUCCEEDED(hr)) { + // Get the size needed first + dwMinSize = WideCharToMultiByte(CP_OEMCP, NULL, pwstrName, -1, nullptr, 0, nullptr, FALSE); + if (dwMinSize == 0) + hr = -1; + } + if (SUCCEEDED(hr)) { + cstrName = new char[dwMinSize]; + WideCharToMultiByte(CP_OEMCP, NULL, pwstrName, -1, cstrName, dwMinSize, NULL, FALSE); + strName = cstrName; + delete[] cstrName; + + deviceList.push_back(strName); + } + + CoTaskMemFree(pwstrName); + + if (FAILED(hr)) { + setError(CAPTUREDEVIDE_ERROR_GETNAMES_FAILED, "Error occurred,gotten " + std::to_string((int)count) + " device names"); + if(pActuallCount) + *pActuallCount = count; + return deviceList; + } + } + if (pActuallCount) + *pActuallCount = count + 1; + return deviceList; +} + +HRESULT WindowsCaptureDevice::setDeviceParam(IMFMediaType *pType) { + HRESULT hr = S_OK; + GUID subtype = { 0 }; + bool getFormat = false; + + switch (type) { + case CAPTUREDEVIDE_TYPE::VIDEO: + hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype); + if (FAILED(hr)) + break; + + for (int i = 0; i < g_cVideoFormats; i++) { + if (subtype == g_VideoFormats[i].MFVideoFormat) { + deviceParam.videoFormat = subtype; + getFormat = true; + break; + } + } + + if (!getFormat) { + for (int i = 0; i < g_cVideoFormats; i++) { + hr = pType->SetGUID(MF_MT_SUBTYPE, g_VideoFormats[i].MFVideoFormat); + if (FAILED(hr)) + continue; + + hr = m_pReader->SetCurrentMediaType( + (DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM, + NULL, + pType + ); + + if (SUCCEEDED(hr)) { + deviceParam.videoFormat = g_VideoFormats[i].MFVideoFormat; + getFormat = true; + break; + } + } + } + if (SUCCEEDED(hr)) + hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &deviceParam.width, &deviceParam.height); + + if (SUCCEEDED(hr)) + hr = GetDefaultStride(pType, &deviceParam.default_stride); + + break; + case CAPTUREDEVIDE_TYPE::AUDIO: + // TODO: + break; + } + + return hr; +} + +void WindowsCaptureDevice::sendMessage(CAPTUREDEVIDE_MESSAGE message) { + // Must be unique lock + std::unique_lock lock(mutex); + messageQueue.push(message); + lock.unlock(); + cond.notify_one(); +} + +CAPTUREDEVIDE_MESSAGE WindowsCaptureDevice::getMessage() { + // Must be unique lock + std::unique_lock lock(mutex); + CAPTUREDEVIDE_MESSAGE message; + cond.wait(lock, [this]() { return !messageQueue.empty(); }); + message = messageQueue.front(); + messageQueue.pop(); + lock.unlock(); + + return message; +} + +void WindowsCaptureDevice::messageHandler() { + CoInitializeEx(NULL, COINIT_MULTITHREADED); + MFStartup(MF_VERSION); + CAPTUREDEVIDE_MESSAGE message; + + if (type == CAPTUREDEVIDE_TYPE::VIDEO) { + setCurrentThreadName("Camera"); + } + + while ((message = getMessage()).command != CAPTUREDEVIDE_COMMAND::SHUTDOWN) { + switch (message.command) { + case CAPTUREDEVIDE_COMMAND::INITIALIZE: + init(); + break; + case CAPTUREDEVIDE_COMMAND::START: + start(); + break; + case CAPTUREDEVIDE_COMMAND::STOP: + stop(); + break; + case CAPTUREDEVIDE_COMMAND::UPDATE_STATE: + updateState((*(CAPTUREDEVIDE_STATE *)message.opacity)); + break; + } + } + + if (state != CAPTUREDEVIDE_STATE::STOPPED) + stop(); + + std::lock_guard lock(sdMutex); + SafeRelease(&m_pSource); + SafeRelease(&m_pReader); + CoTaskMemFree(param.ppDevices); + delete m_pCallback; + unRegisterCMPTMFApis(); + + MFShutdown(); + CoUninitialize(); + + updateState(CAPTUREDEVIDE_STATE::SHUTDOWN); +} + +//----------------------------------------------------------------------------- +// GetDefaultStride +// +// Gets the default stride for a video frame, assuming no extra padding bytes. +// +//----------------------------------------------------------------------------- + +HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride) +{ + LONG lStride = 0; + + // Try to get the default stride from the media type. + HRESULT hr = pType->GetUINT32(MF_MT_DEFAULT_STRIDE, (UINT32*)&lStride); + if (FAILED(hr)) + { + // Attribute not set. Try to calculate the default stride. + GUID subtype = GUID_NULL; + + UINT32 width = 0; + UINT32 height = 0; + + // Get the subtype and the image size. + hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype); + if (SUCCEEDED(hr)) + { + hr = MFGetAttributeSize(pType, MF_MT_FRAME_SIZE, &width, &height); + } + if (SUCCEEDED(hr)) + { + hr = GetStrideForBitmapInfoHeader(subtype.Data1, width, &lStride); + } + + // Set the attribute for later reference. + if (SUCCEEDED(hr)) + { + (void)pType->SetUINT32(MF_MT_DEFAULT_STRIDE, UINT32(lStride)); + } + } + + if (SUCCEEDED(hr)) + { + *plStride = lStride; + } + return hr; +} diff --git a/Windows/CaptureDevice.h b/Windows/CaptureDevice.h new file mode 100644 index 000000000000..ea76a971b59a --- /dev/null +++ b/Windows/CaptureDevice.h @@ -0,0 +1,218 @@ +// Copyright (c) 2020- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#include "libavformat/avformat.h" +#include "libswscale/swscale.h" +#include "libavutil/imgutils.h" +} +#endif // __cplusplus + +struct VideoFormatTransform { + GUID MFVideoFormat; + AVPixelFormat AVVideoFormat; +}; + +enum class CAPTUREDEVIDE_TYPE { + VIDEO, + AUDIO +}; + +enum class CAPTUREDEVIDE_STATE { + UNINITIALIZED, + LOST, + STOPPED, + STARTED, + SHUTDOWN +}; + +enum class CAPTUREDEVIDE_COMMAND { + INITIALIZE, + START, + STOP, + SHUTDOWN, + UPDATE_STATE +}; + +enum CAPTUREDEVIDE_ERROR { + CAPTUREDEVIDE_ERROR_NO_ERROR, + CAPTUREDEVIDE_ERROR_UNKNOWN_TYPE = 0x80000001, + CAPTUREDEVIDE_ERROR_INIT_FAILED, + CAPTUREDEVIDE_ERROR_START_FAILED, + CAPTUREDEVIDE_ERROR_STOP_FAILED, + CAPTUREDEVIDE_ERROR_GETNAMES_FAILED +}; + +struct CAPTUREDEVIDE_MESSAGE{ + CAPTUREDEVIDE_COMMAND command; + void *opacity; +}; + +struct ChooseDeviceParam { + IMFActivate **ppDevices; + UINT32 count; + UINT32 selection; +}; + +union MediaParam { + struct { + UINT32 width; + UINT32 height; + LONG default_stride; + GUID videoFormat; + }; + struct { + UINT32 sampleRate; + UINT32 channels; + LONG padding; + GUID audioFomat; + }; +}; + +template void SafeRelease(T **ppT) { + if (*ppT) { + (*ppT)->Release(); + *ppT = nullptr; + } +} + +class WindowsCaptureDevice; + +class ReaderCallback : public IMFSourceReaderCallback { +public: + ReaderCallback(WindowsCaptureDevice *device); + ~ReaderCallback(); + + // IUnknown methods. + STDMETHODIMP QueryInterface(REFIID iid, void** ppv); + STDMETHODIMP_(ULONG) AddRef() { return 0; } // Unused, just define. + STDMETHODIMP_(ULONG) Release() { return 0; } // Unused, just define. + + // IMFSourceReaderCallback methods. + STDMETHODIMP OnReadSample( + HRESULT hrStatus, + DWORD dwStreamIndex, + DWORD dwStreamFlags, + LONGLONG llTimestamp, + IMFSample *pSample // Can be null,even if hrStatus is success. + ); + + STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) { return S_OK; } + STDMETHODIMP OnFlush(DWORD) { return S_OK; } + + AVPixelFormat getAVVideoFormatbyMFVideoFormat(const GUID &MFVideoFormat); + + /* + * Always convet the image to RGB24 + * @param dst/src pointer to destination/source image + * @param dstW/srcW, dstH/srcH destination/source image's width and height in pixels + * @param dstLineSizes get the linesize of each plane by av_image_fill_linesizes() + * @param srcFormat MF_MT_SUBTYPE attribute of source image + * @param srcPadding should be setted to non-zero if source image has padding + */ + void imgConvert( + unsigned char *dst, unsigned int &dstW, unsigned int &dstH, int dstLineSizes[4], + unsigned char *src, const unsigned int &srcW, const unsigned int &srcH, const GUID &srcFormat, + const int &srcPadding); + + // Flip image start and end in memory, it is neccessary if stride of source image is a negative value + // Might need some tests in different machine. + void imgInvert(unsigned char *dst, unsigned char *src, const int &srcW, const int &srcH, const GUID &srcFormat, const int &srcStride); + void imgInvertRGBA(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h); + void imgInvertRGB(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h); + void imgInvertYUY2(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h); + void imgInvertNV12(unsigned char *dst, int &dstStride, unsigned char *src, const int &srcStride, const int &h); + + +protected: + WindowsCaptureDevice *device; + SwsContext *img_convert_ctx; +}; + +class WindowsCaptureDevice { +public: + WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type); + ~WindowsCaptureDevice(); + + bool init(); + bool start(); + bool stop(); + + CAPTUREDEVIDE_ERROR getError() const { return error; } + 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); + + void setError(const CAPTUREDEVIDE_ERROR &newError, const std::string &newErrorMessage) { error = newError; errorMessage = newErrorMessage; } + void setSelction(const UINT32 &selection) { param.selection = selection; } + void updateState(const CAPTUREDEVIDE_STATE &newState) { state = newState; } + HRESULT setDeviceParam(IMFMediaType *pType); + + bool isShutDown() const { return state == CAPTUREDEVIDE_STATE::SHUTDOWN; } + + void sendMessage(CAPTUREDEVIDE_MESSAGE message); + CAPTUREDEVIDE_MESSAGE getMessage(); + + friend class ReaderCallback; + +protected: +// Handle message here. + void messageHandler(); + + CAPTUREDEVIDE_TYPE type; + MediaParam deviceParam; + MediaParam targetMediaParam; + CAPTUREDEVIDE_STATE state; + ChooseDeviceParam param; + + CAPTUREDEVIDE_ERROR error; + std::string errorMessage; + +// MF interface. + ReaderCallback *m_pCallback; + IMFSourceReader *m_pReader; + IMFMediaSource *m_pSource; + +// Message loop. + std::mutex mutex; + std::condition_variable cond; + std::queue messageQueue; + +// For the shutdown event safety. + std::mutex sdMutex; + +// Camera only + unsigned char *imageRGB; + int imgRGBLineSizes[4]; + unsigned char *imageJpeg; + int imgJpegSize; +}; + +extern WindowsCaptureDevice *winCamera; diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 9eed29a57844..212518524e5f 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -238,7 +238,7 @@ Use Level3 ProgramDatabase - ../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib stdafx.h;Common/DbgNew.h MultiThreadedDebug StreamingSIMDExtensions2 @@ -249,7 +249,7 @@ $(EXTERNAL_COMPILE_OPTIONS) - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;%(AdditionalDependencies) true Windows MachineX86 @@ -275,7 +275,7 @@ Use Level3 ProgramDatabase - ../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib stdafx.h;Common/DbgNew.h MultiThreadedDebug false @@ -287,7 +287,7 @@ $(EXTERNAL_COMPILE_OPTIONS) - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;%(AdditionalDependencies) true $(OutDir)$(ProjectName).pdb true @@ -342,7 +342,7 @@ Use Level3 ProgramDatabase - ../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib stdafx.h MultiThreadedDebug false @@ -354,7 +354,7 @@ $(EXTERNAL_COMPILE_OPTIONS) - oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\arm\lib\avcodec.lib;..\ffmpeg\Windows\arm\lib\avformat.lib;..\ffmpeg\Windows\arm\lib\avutil.lib;..\ffmpeg\Windows\arm\lib\swresample.lib;..\ffmpeg\Windows\arm\lib\swscale.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\arm\lib\avcodec.lib;..\ffmpeg\Windows\arm\lib\avformat.lib;..\ffmpeg\Windows\arm\lib\avutil.lib;..\ffmpeg\Windows\arm\lib\swresample.lib;..\ffmpeg\Windows\arm\lib\swscale.lib;%(AdditionalDependencies) true $(OutDir)$(ProjectName).pdb true @@ -382,7 +382,7 @@ Use Level3 ProgramDatabase - ../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib stdafx.h stdafx.h true @@ -390,7 +390,7 @@ $(EXTERNAL_COMPILE_OPTIONS) - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;%(AdditionalDependencies) $(OutDir)$(TargetName)$(TargetExt) %(AdditionalLibraryDirectories) true @@ -431,14 +431,14 @@ Use Level3 ProgramDatabase - ../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib stdafx.h true false $(EXTERNAL_COMPILE_OPTIONS) - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories) true Windows @@ -514,14 +514,14 @@ Use Level3 ProgramDatabase - ../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib stdafx.h true false $(EXTERNAL_COMPILE_OPTIONS) - oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\arm\lib\avcodec.lib;..\ffmpeg\Windows\arm\lib\avformat.lib;..\ffmpeg\Windows\arm\lib\avutil.lib;..\ffmpeg\Windows\arm\lib\swresample.lib;..\ffmpeg\Windows\arm\lib\swscale.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;oleaut32.lib;comdlg32.lib;shell32.lib;user32.lib;gdi32.lib;advapi32.lib;ole32.lib;Winmm.lib;Ws2_32.lib;dsound.lib;comctl32.lib;d3d9.lib;dxguid.lib;..\ffmpeg\Windows\arm\lib\avcodec.lib;..\ffmpeg\Windows\arm\lib\avformat.lib;..\ffmpeg\Windows\arm\lib\avutil.lib;..\ffmpeg\Windows\arm\lib\swresample.lib;..\ffmpeg\Windows\arm\lib\swscale.lib;%(AdditionalDependencies) %(AdditionalLibraryDirectories) true Windows @@ -680,6 +680,7 @@ true true + @@ -871,6 +872,8 @@ true true + + diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index c640c5642e61..d51c53a9f55a 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -191,6 +191,9 @@ Windows\System + + Windows\Input + @@ -354,6 +357,12 @@ Windows\System + + Windows\Input + + + Windows\Input + diff --git a/headless/Headless.vcxproj b/headless/Headless.vcxproj index dd42cbe4fc0b..94c6d72c8abc 100644 --- a/headless/Headless.vcxproj +++ b/headless/Headless.vcxproj @@ -301,7 +301,7 @@ true true USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions);_CRT_SECURE_NO_WARNINGS - ../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew;../ext/native + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew;../ext/native false StreamingSIMDExtensions2 Precise @@ -316,7 +316,7 @@ true true true - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;dxguid.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86\lib\avcodec.lib;..\ffmpeg\Windows\x86\lib\avformat.lib;..\ffmpeg\Windows\x86\lib\avutil.lib;..\ffmpeg\Windows\x86\lib\swresample.lib;..\ffmpeg\Windows\x86\lib\swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;dxguid.lib;%(AdditionalDependencies) 0x00400000 false true @@ -336,7 +336,7 @@ true true USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_64=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew;../ext/native + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew;../ext/native false NotSet Precise @@ -353,7 +353,7 @@ true true true - Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;dxguid.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Winmm.lib;Ws2_32.lib;opengl32.lib;dsound.lib;glu32.lib;..\ffmpeg\Windows\x86_64\lib\avcodec.lib;..\ffmpeg\Windows\x86_64\lib\avformat.lib;..\ffmpeg\Windows\x86_64\lib\avutil.lib;..\ffmpeg\Windows\x86_64\lib\swresample.lib;..\ffmpeg\Windows\x86_64\lib\swscale.lib;comctl32.lib;d3d9.lib;d3dx9.lib;dxguid.lib;%(AdditionalDependencies) 0x00400000 false true @@ -407,7 +407,7 @@ true true USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_CONSOLE;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew;../ext/native + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../dx9sdk/Include/DX11;../Common;..;../Core;../ext/glew;../ext/native false NotSet Precise @@ -424,7 +424,7 @@ true true true - shell32.lib;advapi32.lib;gdi32.lib;Winmm.lib;Ws2_32.lib;dsound.lib;..\ffmpeg\Windows\arm\lib\avcodec.lib;..\ffmpeg\Windows\arm\lib\avformat.lib;..\ffmpeg\Windows\arm\lib\avutil.lib;..\ffmpeg\Windows\arm\lib\swresample.lib;..\ffmpeg\Windows\arm\lib\swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;shell32.lib;advapi32.lib;gdi32.lib;Winmm.lib;Ws2_32.lib;dsound.lib;..\ffmpeg\Windows\arm\lib\avcodec.lib;..\ffmpeg\Windows\arm\lib\avformat.lib;..\ffmpeg\Windows\arm\lib\avutil.lib;..\ffmpeg\Windows\arm\lib\swresample.lib;..\ffmpeg\Windows\arm\lib\swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) @@ -441,6 +441,7 @@ true true + diff --git a/headless/Headless.vcxproj.filters b/headless/Headless.vcxproj.filters index 962db7ee84c2..f084554e7129 100644 --- a/headless/Headless.vcxproj.filters +++ b/headless/Headless.vcxproj.filters @@ -26,6 +26,9 @@ Other Platforms + + Windows + diff --git a/unittest/UnitTests.vcxproj b/unittest/UnitTests.vcxproj index e9975c81d718..5e3c5629a551 100644 --- a/unittest/UnitTests.vcxproj +++ b/unittest/UnitTests.vcxproj @@ -264,7 +264,7 @@ true true USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib false Size true @@ -277,7 +277,7 @@ true true true - Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;opengl32.lib;dsound.lib;glu32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;opengl32.lib;dsound.lib;glu32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ..\ffmpeg\Windows\x86\lib @@ -291,7 +291,7 @@ true true USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_64=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib false true false @@ -306,7 +306,7 @@ true true true - Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;opengl32.lib;dsound.lib;glu32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;opengl32.lib;dsound.lib;glu32.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ..\ffmpeg\Windows\x86_64\lib @@ -349,7 +349,7 @@ true true USING_WIN_UI;GLEW_STATIC;_CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_ARCH_32=1;_WINDOWS;_UNICODE;UNICODE;%(PreprocessorDefinitions) - ../ext;../common;..;../ext/native;../ext/glew;../ext/zlib + ..\ffmpeg\WindowsInclude;..\ffmpeg\Windows\x86_64\include;../ext;../common;..;../ext/native;../ext/glew;../ext/zlib false true false @@ -364,7 +364,7 @@ true true true - Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) + mf.lib;mfplat.lib;mfreadwrite.lib;mfuuid.lib;shlwapi.lib;Ws2_32.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;dsound.lib;avcodec.lib;avformat.lib;avutil.lib;swresample.lib;swscale.lib;comctl32.lib;d3d9.lib;dxguid.lib;%(AdditionalDependencies) /ignore:4049 /ignore:4217 %(AdditionalOptions) ..\ffmpeg\Windows\arm\lib @@ -376,6 +376,7 @@ true true + true diff --git a/unittest/UnitTests.vcxproj.filters b/unittest/UnitTests.vcxproj.filters index a92e8e080c16..fd685e86b136 100644 --- a/unittest/UnitTests.vcxproj.filters +++ b/unittest/UnitTests.vcxproj.filters @@ -8,10 +8,18 @@ + + Windows + - + + + {584f25d4-e7a1-461f-ba21-7588497a83f1} + + + \ No newline at end of file