mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 23:10:33 +02:00
Windows now uses the same host interface as SDL and iOS: sceUsbMic sends startRecording/stopRecording/deviceChanged, and drains buffered input on the CPU thread with pollRecording. A capture thread asks WASAPI for mono 16-bit at the game's rate (it converts from the mix format, so no more ffmpeg resampling), and copies packets into a lock-free ring buffer. It reopens the device when the selection changes, and retries once a second if the device is missing or gets unplugged. The device list comes from the active capture endpoints. Their friendly names are the ones Media Foundation reported, so existing MicDevice settings still match. An empty setting, now selectable as Auto like on SDL, means the default capture device. The Media Foundation audio capture code is removed; the camera still uses it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
229 lines
6.4 KiB
C++
229 lines
6.4 KiB
C++
// 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 <mfapi.h>
|
|
#include <mfidl.h>
|
|
#include <mfreadwrite.h>
|
|
#include <mutex>
|
|
#include <condition_variable>
|
|
#include <vector>
|
|
#include <queue>
|
|
#include <thread>
|
|
#include <wrl/client.h>
|
|
|
|
#include "Common/CommonTypes.h"
|
|
|
|
#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 CAPTUREDEVICE_TYPE {
|
|
VIDEO,
|
|
};
|
|
|
|
enum class CAPTUREDEVICE_STATE {
|
|
UNINITIALIZED,
|
|
LOST,
|
|
STOPPED,
|
|
STARTED,
|
|
SHUTDOWN
|
|
};
|
|
|
|
enum class CAPTUREDEVICE_COMMAND {
|
|
START,
|
|
STOP,
|
|
SHUTDOWN,
|
|
UPDATE_STATE
|
|
};
|
|
|
|
enum CAPTUREDEVICE_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 CAPTUREDEVICE_MESSAGE {
|
|
CAPTUREDEVICE_COMMAND command;
|
|
void *opacity;
|
|
};
|
|
|
|
struct ChooseDeviceParam {
|
|
IMFActivate **ppDevices;
|
|
UINT32 count;
|
|
UINT32 selection;
|
|
};
|
|
|
|
union MediaParam {
|
|
struct {
|
|
UINT32 width;
|
|
UINT32 height;
|
|
LONG default_stride;
|
|
GUID videoFormat;
|
|
};
|
|
};
|
|
|
|
template <class T> void SafeRelease(T **ppT) {
|
|
if (*ppT) {
|
|
(*ppT)->Release();
|
|
*ppT = nullptr;
|
|
}
|
|
}
|
|
|
|
class WindowsCaptureDevice;
|
|
|
|
class ReaderCallback final : public IMFSourceReaderCallback {
|
|
public:
|
|
ReaderCallback(WindowsCaptureDevice *device);
|
|
virtual ~ReaderCallback();
|
|
|
|
// IUnknown methods.
|
|
STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override;
|
|
STDMETHODIMP_(ULONG) AddRef() override { return 0; } // Unused, just define.
|
|
STDMETHODIMP_(ULONG) Release() override { 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.
|
|
) override;
|
|
|
|
STDMETHODIMP OnEvent(DWORD, IMFMediaEvent *) override { return S_OK; }
|
|
STDMETHODIMP OnFlush(DWORD) override { return S_OK; }
|
|
|
|
AVPixelFormat getAVVideoFormatbyMFVideoFormat(const GUID &MFVideoFormat);
|
|
|
|
/*
|
|
* Always convert 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 = nullptr;
|
|
};
|
|
|
|
class WindowsCaptureDevice {
|
|
public:
|
|
WindowsCaptureDevice(CAPTUREDEVICE_TYPE type);
|
|
~WindowsCaptureDevice();
|
|
|
|
void CheckDevices();
|
|
|
|
bool start(void *startParam);
|
|
bool stop();
|
|
|
|
CAPTUREDEVICE_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<std::string> getDeviceList(bool forceEnum = false, int *pActuallCount = nullptr);
|
|
|
|
void setError(const CAPTUREDEVICE_ERROR &newError, const std::string &newErrorMessage) { error = newError; errorMessage = newErrorMessage; }
|
|
void setSelection(const UINT32 &selection) { param.selection = selection; }
|
|
HRESULT setDeviceParam(IMFMediaType *pType);
|
|
|
|
bool isShutDown() const { return state == CAPTUREDEVICE_STATE::SHUTDOWN; }
|
|
bool isStarted() const { return state == CAPTUREDEVICE_STATE::STARTED; }
|
|
void waitShutDown();
|
|
|
|
void sendMessage(CAPTUREDEVICE_MESSAGE message);
|
|
CAPTUREDEVICE_MESSAGE getMessage();
|
|
|
|
HRESULT enumDevices();
|
|
|
|
friend class ReaderCallback;
|
|
|
|
protected:
|
|
void updateState(const CAPTUREDEVICE_STATE &newState);
|
|
// Handle message here.
|
|
void messageHandler();
|
|
|
|
CAPTUREDEVICE_TYPE type;
|
|
MediaParam deviceParam;
|
|
MediaParam targetMediaParam;
|
|
CAPTUREDEVICE_STATE state;
|
|
ChooseDeviceParam param;
|
|
|
|
CAPTUREDEVICE_ERROR error;
|
|
std::string errorMessage;
|
|
|
|
bool isDeviceChanged = false;
|
|
|
|
// MF interface.
|
|
Microsoft::WRL::ComPtr<ReaderCallback> m_pCallback;
|
|
Microsoft::WRL::ComPtr<IMFSourceReader> m_pReader;
|
|
Microsoft::WRL::ComPtr<IMFMediaSource> m_pSource;
|
|
|
|
// Message loop.
|
|
std::mutex mutex;
|
|
std::condition_variable cond;
|
|
std::queue<CAPTUREDEVICE_MESSAGE> messageQueue;
|
|
|
|
// For the shutdown event safety.
|
|
std::mutex sdMutex;
|
|
|
|
// Param updating synchronously.
|
|
std::mutex paramMutex;
|
|
std::mutex stateMutex_;
|
|
std::condition_variable stateCond_;
|
|
|
|
unsigned char *imageRGB = nullptr;
|
|
int imgRGBLineSizes[4]{};
|
|
unsigned char *imageJpeg = nullptr;
|
|
int imgJpegSize = 0;
|
|
|
|
std::thread thread_;
|
|
};
|
|
|
|
extern WindowsCaptureDevice *winCamera;
|
|
|
|
bool RegisterCMPTMFApis();
|
|
bool UnRegisterCMPTMFApis();
|