mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-10-07 19:03:29 +00:00
Merge pull request #13229 from shenweip/UsbMic
Add basic Microphone support on Windows.
This commit is contained in:
commit
45b05b386b
@ -142,6 +142,7 @@ public:
|
||||
std::string sVulkanDevice;
|
||||
std::string sD3D11Device; // Windows only
|
||||
std::string sCameraDevice;
|
||||
std::string sMicDevice;
|
||||
|
||||
bool bSoftwareRendering;
|
||||
bool bHardwareTransform; // only used in the GLES backend
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "Core/HLE/FunctionWrappers.h"
|
||||
#include "Core/HLE/sceKernelThread.h"
|
||||
#include "Core/HLE/sceAudio.h"
|
||||
#include "Core/HLE/sceUsbMic.h"
|
||||
#include "Core/HLE/__sceAudio.h"
|
||||
#include "Core/Reporting.h"
|
||||
|
||||
@ -471,6 +472,11 @@ static u32 sceAudioSRCOutputBlocking(u32 vol, u32 buf) {
|
||||
return hleLogSuccessI(SCEAUDIO, result);
|
||||
}
|
||||
|
||||
static int sceAudioInputBlocking(u32 maxSamples, u32 sampleRate, u32 bufAddr) {
|
||||
ERROR_LOG(HLE, "UNIMPL sceAudioInputBlocking: maxSamples: %d, samplerate: %d, bufAddr: %08x", maxSamples, sampleRate, bufAddr);
|
||||
return __MicInputBlocking(maxSamples, sampleRate, bufAddr);
|
||||
}
|
||||
|
||||
static u32 sceAudioRoutingSetMode(u32 mode) {
|
||||
ERROR_LOG_REPORT(SCEAUDIO, "sceAudioRoutingSetMode(%08x)", mode);
|
||||
int previousMode = defaultRoutingMode;
|
||||
@ -535,7 +541,7 @@ const HLEFunction sceAudio[] =
|
||||
{0X7DE61688, nullptr, "sceAudioInputInit", '?', "" },
|
||||
{0XE926D3FB, nullptr, "sceAudioInputInitEx", '?', "" },
|
||||
{0X6D4BEC68, nullptr, "sceAudioInput", '?', "" },
|
||||
{0X086E5895, nullptr, "sceAudioInputBlocking", '?', "" },
|
||||
{0X086E5895, &WrapI_UUU<sceAudioInputBlocking>, "sceAudioInputBlocking", 'i', "xxx" },
|
||||
{0XA708C6A6, nullptr, "sceAudioGetInputLength", '?', "" },
|
||||
{0XA633048E, nullptr, "sceAudioPollInputEnd", '?', "" },
|
||||
{0X87B2E651, nullptr, "sceAudioWaitInputEnd", '?', "" },
|
||||
|
@ -77,12 +77,13 @@
|
||||
#include "sceImpose.h"
|
||||
#include "sceUsb.h"
|
||||
#include "sceUsbGps.h"
|
||||
#include "sceUsbCam.h"
|
||||
#include "sceUsbMic.h"
|
||||
#include "scePspNpDrm_user.h"
|
||||
#include "sceVaudio.h"
|
||||
#include "sceHeap.h"
|
||||
#include "sceDmac.h"
|
||||
#include "sceMp4.h"
|
||||
#include "sceUsbCam.h"
|
||||
|
||||
#include "../Util/PPGeDraw.h"
|
||||
|
||||
@ -146,6 +147,7 @@ void __KernelInit()
|
||||
__VideoPmpInit();
|
||||
__UsbGpsInit();
|
||||
__UsbCamInit();
|
||||
__UsbMicInit();
|
||||
|
||||
SaveState::Init(); // Must be after IO, as it may create a directory
|
||||
Reporting::Init();
|
||||
@ -170,6 +172,7 @@ void __KernelShutdown()
|
||||
kernelObjects.Clear();
|
||||
|
||||
__UsbCamShutdown();
|
||||
__UsbMicShutdown();
|
||||
__UsbGpsShutdown();
|
||||
|
||||
__AudioCodecShutdown();
|
||||
@ -278,6 +281,7 @@ void __KernelDoState(PointerWrap &p)
|
||||
__VideoPmpDoState(p);
|
||||
__AACDoState(p);
|
||||
__UsbGpsDoState(p);
|
||||
__UsbMicDoState(p);
|
||||
|
||||
// IMPORTANT! Add new sections last!
|
||||
}
|
||||
|
@ -79,6 +79,7 @@ const WaitTypeNames waitTypeNames[] = {
|
||||
{ WAITTYPE_TLSPL, "TLS" },
|
||||
{ WAITTYPE_VMEM, "Volatile Mem" },
|
||||
{ WAITTYPE_ASYNCIO, "AsyncIO" },
|
||||
{ WAITTYPE_MICINPUT, "Microphone input"},
|
||||
};
|
||||
|
||||
const char *getWaitTypeName(WaitType type)
|
||||
|
@ -106,6 +106,7 @@ enum WaitType : int
|
||||
WAITTYPE_TLSPL = 21,
|
||||
WAITTYPE_VMEM = 22,
|
||||
WAITTYPE_ASYNCIO = 23,
|
||||
WAITTYPE_MICINPUT = 24, // fake
|
||||
|
||||
NUM_WAITTYPES
|
||||
};
|
||||
|
@ -83,7 +83,7 @@ static int sceUsbGetState() {
|
||||
| (usbConnected ? USB_STATUS_CONNECTED : USB_STATUS_DISCONNECTED)
|
||||
| (usbActivated ? USB_STATUS_ACTIVATED : USB_STATUS_DEACTIVATED);
|
||||
}
|
||||
INFO_LOG(HLE, "sceUsbGetState: 0x%x", state);
|
||||
DEBUG_LOG(HLE, "sceUsbGetState: 0x%x", state);
|
||||
return state;
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
#include "Core/HLE/sceUsbCam.h"
|
||||
#include "Core/HLE/sceUsbMic.h"
|
||||
#include "Core/HW/Camera.h"
|
||||
#include "Core/MemMapHelpers.h"
|
||||
|
||||
@ -112,7 +113,7 @@ static int getCameraResolution(Camera::ConfigType type, int *width, int *height)
|
||||
|
||||
|
||||
static int sceUsbCamSetupMic(u32 paramAddr, u32 workareaAddr, int wasize) {
|
||||
INFO_LOG(HLE, "UNIMPL sceUsbCamSetupMic");
|
||||
INFO_LOG(HLE, "sceUsbCamSetupMic");
|
||||
if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupMicParam))) {
|
||||
Memory::ReadStruct(paramAddr, &config->micParam);
|
||||
}
|
||||
@ -131,13 +132,7 @@ static int sceUsbCamStopMic() {
|
||||
|
||||
static int sceUsbCamReadMicBlocking(u32 bufAddr, u32 size) {
|
||||
INFO_LOG(HLE, "UNIMPL sceUsbCamReadMicBlocking: size: %d", size);
|
||||
for (unsigned int i = 0; i < size; i++) {
|
||||
if (Memory::IsValidAddress(bufAddr + i)) {
|
||||
Memory::Write_U8(i & 0xFF, bufAddr + i);
|
||||
}
|
||||
}
|
||||
hleEatMicro(1000000 / config->micParam.frequency * (size / 2));
|
||||
return size;
|
||||
return __MicInputBlocking(size >> 1, config->micParam.frequency, bufAddr);
|
||||
}
|
||||
|
||||
static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) {
|
||||
|
@ -21,29 +21,408 @@
|
||||
#include "Common/ChunkFile.h"
|
||||
#include "Core/HLE/HLE.h"
|
||||
#include "Core/HLE/FunctionWrappers.h"
|
||||
#include "Core/HLE/sceKernelThread.h"
|
||||
#include "Core/HLE/sceUsbMic.h"
|
||||
#include "Core/CoreTiming.h"
|
||||
#include "Core/MemMapHelpers.h"
|
||||
|
||||
static int sceUsbMicInputBlocking(u32 size, u32 samplerate, u32 bufAddr) {
|
||||
INFO_LOG(HLE, "UNIMPL sceUsbMicInputBlocking: size: %d, samplerate: %d", size, samplerate);
|
||||
for (unsigned int i = 0; i < size; i++) {
|
||||
if (Memory::IsValidAddress(bufAddr + i)) {
|
||||
Memory::Write_U8(i & 0xFF, bufAddr + i);
|
||||
#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP) && !defined(__LIBRETRO__)
|
||||
#define HAVE_WIN32_MICROPHONE
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_WIN32_MICROPHONE
|
||||
#include "Windows/CaptureDevice.h"
|
||||
#endif
|
||||
|
||||
enum {
|
||||
SCE_USBMIC_ERROR_INVALID_MAX_SAMPLES = 0x80243806,
|
||||
SCE_USBMIC_ERROR_INVALID_SAMPLERATE = 0x8024380A,
|
||||
};
|
||||
|
||||
int eventUsbMicAudioUpdate = -1;
|
||||
|
||||
QueueBuf *audioBuf = nullptr;
|
||||
u32 numNeedSamples;
|
||||
static std::vector<MicWaitInfo> waitingThreads;
|
||||
std::mutex wtMutex;
|
||||
bool isNeedInput;
|
||||
u32 curSampleRate;
|
||||
u32 curChannels;
|
||||
int micState; // 0 means stopped, 1 means started, for save state.
|
||||
|
||||
static void __UsbMicAudioUpdate(u64 userdata, int cyclesLate) {
|
||||
SceUID threadID = (SceUID)userdata;
|
||||
u32 error;
|
||||
int count = 0;
|
||||
std::unique_lock<std::mutex> lock(wtMutex);
|
||||
for (auto waitingThread : waitingThreads) {
|
||||
if (waitingThread.threadID == threadID) {
|
||||
SceUID waitID = __KernelGetWaitID(threadID, WAITTYPE_MICINPUT, error);
|
||||
if (waitID == 0)
|
||||
continue;
|
||||
if (Microphone::isHaveDevice()) {
|
||||
if (Microphone::availableAudioBufSize() >= waitingThread.needSize) {
|
||||
u8 *tempbuf8 = new u8[waitingThread.needSize];
|
||||
Microphone::getAudioData(tempbuf8, waitingThread.needSize);
|
||||
Memory::Memcpy(waitingThread.addr, tempbuf8, waitingThread.needSize);
|
||||
delete[] tempbuf8;
|
||||
u32 ret = __KernelGetWaitValue(threadID, error);
|
||||
DEBUG_LOG(HLE, "sceUsbMic: Waking up thread(%d)", (int)waitingThread.threadID);
|
||||
__KernelResumeThreadFromWait(threadID, ret);
|
||||
waitingThreads.erase(waitingThreads.begin() + count);
|
||||
if (waitingThreads.size() == 0)
|
||||
isNeedInput = false;
|
||||
} else {
|
||||
u64 waitTimeus = (waitingThread.needSize - Microphone::availableAudioBufSize()) * 1000000 / 2 / waitingThread.sampleRate;
|
||||
CoreTiming::ScheduleEvent(usToCycles(waitTimeus), eventUsbMicAudioUpdate, userdata);
|
||||
}
|
||||
} else {
|
||||
for (u32 i = 0; i < waitingThread.needSize; i++) {
|
||||
if (Memory::IsValidAddress(waitingThread.addr + i)) {
|
||||
Memory::Write_U8(i & 0xFF, waitingThread.addr + i);
|
||||
}
|
||||
}
|
||||
u32 ret = __KernelGetWaitValue(threadID, error);
|
||||
DEBUG_LOG(HLE, "sceUsbMic: Waking up thread(%d)", (int)waitingThread.threadID);
|
||||
__KernelResumeThreadFromWait(threadID, ret);
|
||||
waitingThreads.erase(waitingThreads.begin() + count);
|
||||
if (waitingThreads.size() == 0)
|
||||
isNeedInput = false;
|
||||
}
|
||||
}
|
||||
++count;
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
void __UsbMicInit() {
|
||||
if (audioBuf) {
|
||||
delete audioBuf;
|
||||
audioBuf = nullptr;
|
||||
}
|
||||
numNeedSamples = 0;
|
||||
waitingThreads.clear();
|
||||
isNeedInput = false;
|
||||
curSampleRate = 44100;
|
||||
curChannels = 1;
|
||||
micState = 0;
|
||||
eventUsbMicAudioUpdate = CoreTiming::RegisterEvent("UsbMicAudioUpdate", &__UsbMicAudioUpdate);
|
||||
}
|
||||
|
||||
void __UsbMicShutdown() {
|
||||
if (audioBuf) {
|
||||
delete audioBuf;
|
||||
audioBuf = nullptr;
|
||||
}
|
||||
Microphone::stopMic();
|
||||
}
|
||||
|
||||
void __UsbMicDoState(PointerWrap &p) {
|
||||
auto s = p.Section("sceUsbMic", 1, 1);
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
p.Do(numNeedSamples);
|
||||
p.Do(waitingThreads);
|
||||
p.Do(isNeedInput);
|
||||
p.Do(curSampleRate);
|
||||
p.Do(curChannels);
|
||||
p.Do(micState);
|
||||
// Maybe also need to save the state of audioBuf.
|
||||
if (waitingThreads.size() != 0 && p.mode == PointerWrap::MODE_READ) {
|
||||
u64 waitTimeus = (waitingThreads[0].needSize - Microphone::availableAudioBufSize()) * 1000000 / 2 / waitingThreads[0].sampleRate;
|
||||
CoreTiming::ScheduleEvent(usToCycles(waitTimeus), eventUsbMicAudioUpdate, waitingThreads[0].threadID);
|
||||
}
|
||||
if (micState == 0) {
|
||||
if (Microphone::isMicStarted())
|
||||
Microphone::stopMic();
|
||||
} else if (micState == 1) {
|
||||
if (Microphone::isMicStarted()) {
|
||||
Microphone::stopMic();
|
||||
Microphone::startMic(new std::vector<u32>({ curSampleRate, curChannels }));
|
||||
} else {
|
||||
Microphone::startMic(new std::vector<u32>({ curSampleRate, curChannels }));
|
||||
}
|
||||
}
|
||||
hleEatMicro(1000000 / samplerate * (size / 2));
|
||||
}
|
||||
|
||||
QueueBuf::QueueBuf(u32 size) : start(0), end(0), capacity(size) {
|
||||
buf_ = new u8[size];
|
||||
}
|
||||
|
||||
QueueBuf::~QueueBuf() {
|
||||
delete[] buf_;
|
||||
}
|
||||
|
||||
QueueBuf::QueueBuf(const QueueBuf &buf) {
|
||||
buf_ = new u8[buf.capacity];
|
||||
memcpy(buf_, buf.buf_, buf.capacity);
|
||||
start = buf.start;
|
||||
end = buf.end;
|
||||
capacity = buf.capacity;
|
||||
}
|
||||
|
||||
QueueBuf& QueueBuf::operator=(const QueueBuf &buf) {
|
||||
if (capacity < buf.capacity) {
|
||||
resize(buf.capacity);
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
memcpy(buf_, buf.buf_, buf.capacity);
|
||||
start = buf.start;
|
||||
end = buf.end;
|
||||
lock.unlock();
|
||||
return *this;
|
||||
}
|
||||
|
||||
void QueueBuf::push(u8 *buf, u32 size) {
|
||||
if (getRemainingSize() < size) {
|
||||
resize((capacity + size - getRemainingSize()) * 3 / 2);
|
||||
}
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (end + size <= capacity) {
|
||||
memcpy(buf_ + end, buf, size);
|
||||
end += size;
|
||||
} else {
|
||||
memcpy(buf_ + end, buf, capacity - end);
|
||||
size -= capacity - end;
|
||||
memcpy(buf_, buf + capacity - end, size);
|
||||
end = size;
|
||||
}
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
u32 QueueBuf::pop(u8 *buf, u32 size) {
|
||||
u32 ret = 0;
|
||||
if (getAvailableSize() < size)
|
||||
size = getAvailableSize();
|
||||
ret = size;
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
if (start + size <= capacity) {
|
||||
memcpy(buf, buf_ + start, size);
|
||||
start += size;
|
||||
} else {
|
||||
memcpy(buf, buf_ + start, capacity - start);
|
||||
size -= capacity - start;
|
||||
memcpy(buf + capacity - start, buf_, size);
|
||||
start = size;
|
||||
}
|
||||
lock.unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void QueueBuf::resize(u32 newSize) {
|
||||
if (capacity >= newSize) {
|
||||
return;
|
||||
}
|
||||
u32 availableSize = getAvailableSize();
|
||||
u8 *oldbuf = buf_;
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
buf_ = new u8[newSize];
|
||||
if (end >= start) {
|
||||
memcpy(buf_, oldbuf + start, availableSize);
|
||||
} else {
|
||||
memcpy(buf_, oldbuf + start, capacity - start);
|
||||
memcpy(buf_ + capacity - start, oldbuf, availableSize - (capacity - start));
|
||||
}
|
||||
start = 0;
|
||||
end = availableSize;
|
||||
capacity = newSize;
|
||||
delete[] oldbuf;
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
void QueueBuf::flush() {
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
start = 0;
|
||||
end = 0;
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
u32 QueueBuf::getAvailableSize() {
|
||||
u32 availableSize = 0;
|
||||
if (end >= start) {
|
||||
availableSize = end - start;
|
||||
} else {
|
||||
availableSize = end + capacity - start;
|
||||
}
|
||||
return availableSize;
|
||||
}
|
||||
|
||||
u32 QueueBuf::getRemainingSize() {
|
||||
return capacity - getAvailableSize();
|
||||
}
|
||||
|
||||
static int sceUsbMicPollInputEnd() {
|
||||
ERROR_LOG(HLE, "UNIMPL sceUsbMicPollInputEnd");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sceUsbMicInputBlocking(u32 maxSamples, u32 sampleRate, u32 bufAddr) {
|
||||
INFO_LOG(HLE, "sceUsbMicInputBlocking: maxSamples: %d, samplerate: %d, bufAddr: %08x", maxSamples, sampleRate, bufAddr);
|
||||
if (maxSamples <= 0 || (maxSamples & 0x3F) != 0) {
|
||||
return SCE_USBMIC_ERROR_INVALID_MAX_SAMPLES;
|
||||
}
|
||||
|
||||
if (sampleRate != 44100 && sampleRate != 22050 && sampleRate != 11025) {
|
||||
return SCE_USBMIC_ERROR_INVALID_SAMPLERATE;
|
||||
}
|
||||
curSampleRate = sampleRate;
|
||||
curChannels = 1;
|
||||
return __MicInputBlocking(maxSamples, sampleRate, bufAddr);
|
||||
}
|
||||
|
||||
static int sceUsbMicInputInitEx(u32 paramAddr) {
|
||||
ERROR_LOG(HLE, "UNIMPL sceUsbMicInputInitEx: %08x", paramAddr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sceUsbMicInput() {
|
||||
ERROR_LOG(HLE, "UNIMPL sceUsbMicInput");
|
||||
return 0;
|
||||
}
|
||||
static int sceUsbMicGetInputLength() {
|
||||
int ret = Microphone::availableAudioBufSize() / 2;
|
||||
ERROR_LOG(HLE, "UNTEST sceUsbMicGetInputLength(ret: %d)", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sceUsbMicInputInit(int unknown1, int inputVolume, int unknown2) {
|
||||
ERROR_LOG(HLE, "UNIMPL sceUsbMicInputInit(unknown1: %d, inputVolume: %d, unknown2: %d)", unknown1, inputVolume, unknown2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sceUsbMicWaitInputEnd() {
|
||||
ERROR_LOG(HLE, "UNIMPL sceUsbMicWaitInputEnd");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Microphone::startMic(void *param) {
|
||||
#ifdef HAVE_WIN32_MICROPHONE
|
||||
if (winMic)
|
||||
winMic->sendMessage({ CAPTUREDEVIDE_COMMAND::START, param });
|
||||
#endif
|
||||
micState = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Microphone::stopMic() {
|
||||
#ifdef HAVE_WIN32_MICROPHONE
|
||||
if (winMic)
|
||||
winMic->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr });
|
||||
#endif
|
||||
micState = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Microphone::isHaveDevice() {
|
||||
#ifdef HAVE_WIN32_MICROPHONE
|
||||
return winMic->getDeviceCounts() >= 1;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Microphone::isMicStarted() {
|
||||
#ifdef HAVE_WIN32_MICROPHONE
|
||||
if(winMic)
|
||||
return winMic->isStarted();
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Microphone::isNeedInput() {
|
||||
return ::isNeedInput;
|
||||
}
|
||||
|
||||
u32 Microphone::numNeedSamples() {
|
||||
return ::numNeedSamples;
|
||||
}
|
||||
|
||||
u32 Microphone::availableAudioBufSize() {
|
||||
return audioBuf->getAvailableSize();
|
||||
}
|
||||
|
||||
int Microphone::addAudioData(u8 *buf, u32 size) {
|
||||
if (audioBuf)
|
||||
audioBuf->push(buf, size);
|
||||
else
|
||||
return 0;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
u32 Microphone::getAudioData(u8 *buf, u32 size) {
|
||||
if(audioBuf)
|
||||
return audioBuf->pop(buf, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Microphone::flushAudioData() {
|
||||
audioBuf->flush();
|
||||
}
|
||||
|
||||
std::vector<std::string> Microphone::getDeviceList() {
|
||||
#ifdef HAVE_WIN32_MICROPHONE
|
||||
if (winMic) {
|
||||
return winMic->getDeviceList();
|
||||
}
|
||||
#endif
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
void Microphone::onMicDeviceChange() {
|
||||
if (Microphone::isMicStarted()) {
|
||||
Microphone::stopMic();
|
||||
// Just use the last param.
|
||||
Microphone::startMic(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
u32 __MicInputBlocking(u32 maxSamples, u32 sampleRate, u32 bufAddr) {
|
||||
u32 size = maxSamples << 1;
|
||||
if (size > numNeedSamples << 1) {
|
||||
if (!audioBuf) {
|
||||
audioBuf = new QueueBuf(size);
|
||||
} else {
|
||||
audioBuf->resize(size);
|
||||
}
|
||||
}
|
||||
if (!audioBuf)
|
||||
return 0;
|
||||
|
||||
numNeedSamples = maxSamples;
|
||||
Microphone::flushAudioData();
|
||||
if (!Microphone::isMicStarted()) {
|
||||
std::vector<u32> *param = new std::vector<u32>({ sampleRate, 1 });
|
||||
Microphone::startMic(param);
|
||||
}
|
||||
u64 waitTimeus = 0;
|
||||
if (Microphone::availableAudioBufSize() < size) {
|
||||
waitTimeus = (size - Microphone::availableAudioBufSize()) * 1000000 / 2 / sampleRate;
|
||||
isNeedInput = true;
|
||||
}
|
||||
CoreTiming::ScheduleEvent(usToCycles(waitTimeus), eventUsbMicAudioUpdate, __KernelGetCurThread());
|
||||
MicWaitInfo waitInfo = { __KernelGetCurThread(), bufAddr, size, sampleRate };
|
||||
std::unique_lock<std::mutex> lock(wtMutex);
|
||||
waitingThreads.push_back(waitInfo);
|
||||
lock.unlock();
|
||||
DEBUG_LOG(HLE, "MicInputBlocking: blocking thread(%d)", (int)__KernelGetCurThread());
|
||||
__KernelWaitCurThread(WAITTYPE_MICINPUT, 1, size, 0, false, "blocking microphone");
|
||||
|
||||
return maxSamples;
|
||||
}
|
||||
|
||||
const HLEFunction sceUsbMic[] =
|
||||
{
|
||||
{0x06128E42, nullptr, "sceUsbMicPollInputEnd", '?', "" },
|
||||
{0x06128E42, &WrapI_V<sceUsbMicPollInputEnd>, "sceUsbMicPollInputEnd", 'i', "" },
|
||||
{0x2E6DCDCD, &WrapI_UUU<sceUsbMicInputBlocking>, "sceUsbMicInputBlocking", 'i', "xxx" },
|
||||
{0x45310F07, nullptr, "sceUsbMicInputInitEx", '?', "" },
|
||||
{0x5F7F368D, nullptr, "sceUsbMicInput", '?', "" },
|
||||
{0x63400E20, nullptr, "sceUsbMicGetInputLength", '?', "" },
|
||||
{0xB8E536EB, nullptr, "sceUsbMicInputInit", '?', "" },
|
||||
{0xF899001C, nullptr, "sceUsbMicWaitInputEnd", '?', "" },
|
||||
{0x45310F07, &WrapI_U<sceUsbMicInputInitEx>, "sceUsbMicInputInitEx", 'i', "x" },
|
||||
{0x5F7F368D, &WrapI_V<sceUsbMicInput> , "sceUsbMicInput", 'i', "" },
|
||||
{0x63400E20, &WrapI_V<sceUsbMicGetInputLength>, "sceUsbMicGetInputLength", 'i', "" },
|
||||
{0xB8E536EB, &WrapI_III<sceUsbMicInputInit>, "sceUsbMicInputInit", 'i', "iii" },
|
||||
{0xF899001C, &WrapI_V<sceUsbMicWaitInputEnd>, "sceUsbMicWaitInputEnd", 'i', "" },
|
||||
};
|
||||
|
||||
void Register_sceUsbMic()
|
||||
|
@ -16,4 +16,61 @@
|
||||
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "sceKernel.h"
|
||||
#include <mutex>
|
||||
|
||||
void Register_sceUsbMic();
|
||||
|
||||
void __UsbMicInit();
|
||||
void __UsbMicShutdown();
|
||||
void __UsbMicDoState(PointerWrap &p);
|
||||
|
||||
struct MicWaitInfo {
|
||||
SceUID threadID;
|
||||
u32 addr;
|
||||
u32 needSize;
|
||||
u32 sampleRate;
|
||||
};
|
||||
|
||||
class QueueBuf {
|
||||
public:
|
||||
QueueBuf(u32 size);
|
||||
~QueueBuf();
|
||||
|
||||
QueueBuf(const QueueBuf &buf);
|
||||
QueueBuf& operator=(const QueueBuf &buf);
|
||||
|
||||
void push(u8 *buf, u32 size);
|
||||
u32 pop(u8 *buf, u32 size);
|
||||
void resize(u32 newSize);
|
||||
void flush();
|
||||
u32 getAvailableSize();
|
||||
u32 getRemainingSize();
|
||||
|
||||
private:
|
||||
u32 start;
|
||||
u32 end;
|
||||
u32 capacity;
|
||||
u8 *buf_;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
namespace Microphone {
|
||||
int startMic(void *param);
|
||||
int stopMic();
|
||||
bool isHaveDevice();
|
||||
bool isMicStarted();
|
||||
bool isNeedInput();
|
||||
u32 numNeedSamples();
|
||||
u32 availableAudioBufSize();
|
||||
|
||||
int addAudioData(u8 *buf, u32 size);
|
||||
u32 getAudioData(u8 *buf, u32 size);
|
||||
void flushAudioData();
|
||||
|
||||
std::vector<std::string> getDeviceList();
|
||||
void onMicDeviceChange();
|
||||
}
|
||||
|
||||
u32 __MicInputBlocking(u32 maxSamples, u32 sampleRate, u32 bufAddr);
|
||||
|
@ -60,6 +60,7 @@
|
||||
#include "Core/TextureReplacer.h"
|
||||
#include "Core/WebServer.h"
|
||||
#include "Core/HLE/sceUsbCam.h"
|
||||
#include "Core/HLE/sceUsbMic.h"
|
||||
#include "GPU/Common/PostShader.h"
|
||||
#include "android/jni/TestRunner.h"
|
||||
#include "GPU/GPUInterface.h"
|
||||
@ -547,6 +548,13 @@ void GameSettingsScreen::CreateViews() {
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<std::string> micList = Microphone::getDeviceList();
|
||||
if (micList.size() >= 1) {
|
||||
audioSettings->Add(new ItemHeader(gr->T("Microphone")));
|
||||
PopupMultiChoiceDynamic *MicChoice = audioSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sMicDevice, gr->T("Microphone Device"), micList, nullptr, screenManager()));
|
||||
MicChoice->OnChoice.Handle(this, &GameSettingsScreen::OnMicDeviceChange);
|
||||
}
|
||||
|
||||
#if defined(SDL)
|
||||
std::vector<std::string> audioDeviceList;
|
||||
SplitString(System_GetProperty(SYSPROP_AUDIO_DEVICE_LIST), '\0', audioDeviceList);
|
||||
@ -1312,6 +1320,11 @@ UI::EventReturn GameSettingsScreen::OnCameraDeviceChange(UI::EventParams& e) {
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
|
||||
UI::EventReturn GameSettingsScreen::OnMicDeviceChange(UI::EventParams& e) {
|
||||
Microphone::onMicDeviceChange();
|
||||
return UI::EVENT_DONE;
|
||||
}
|
||||
|
||||
UI::EventReturn GameSettingsScreen::OnAudioDevice(UI::EventParams &e) {
|
||||
auto a = GetI18NCategory("Audio");
|
||||
if (g_Config.sAudioDevice == a->T("Auto")) {
|
||||
|
@ -103,6 +103,7 @@ private:
|
||||
UI::EventReturn OnRenderingDevice(UI::EventParams &e);
|
||||
UI::EventReturn OnInflightFramesChoice(UI::EventParams &e);
|
||||
UI::EventReturn OnCameraDeviceChange(UI::EventParams& e);
|
||||
UI::EventReturn OnMicDeviceChange(UI::EventParams& e);
|
||||
UI::EventReturn OnAudioDevice(UI::EventParams &e);
|
||||
UI::EventReturn OnJitAffectingSetting(UI::EventParams &e);
|
||||
#if PPSSPP_PLATFORM(ANDROID)
|
||||
|
@ -864,6 +864,8 @@ bool NativeInitGraphics(GraphicsContext *graphicsContext) {
|
||||
if (IsWin7OrHigher()) {
|
||||
winCamera = new WindowsCaptureDevice(CAPTUREDEVIDE_TYPE::VIDEO);
|
||||
winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::INITIALIZE, nullptr });
|
||||
winMic = new WindowsCaptureDevice(CAPTUREDEVIDE_TYPE::AUDIO);
|
||||
winMic->sendMessage({ CAPTUREDEVIDE_COMMAND::INITIALIZE, nullptr });
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -937,6 +939,12 @@ void NativeShutdownGraphics() {
|
||||
delete winCamera;
|
||||
winCamera = nullptr;
|
||||
}
|
||||
if (winMic) {
|
||||
winMic->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr });
|
||||
while (!winMic->isShutDown()) {};// Wait for shutting down.
|
||||
delete winMic;
|
||||
winMic = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
UIBackgroundShutdown();
|
||||
|
@ -24,8 +24,6 @@
|
||||
#include "Core/HLE/sceUsbCam.h"
|
||||
#include "Core/Config.h"
|
||||
|
||||
bool isDeviceChanged = false;
|
||||
|
||||
namespace MFAPI {
|
||||
HINSTANCE Mflib;
|
||||
HINSTANCE Mfplatlib;
|
||||
@ -87,6 +85,7 @@ bool unRegisterCMPTMFApis() {
|
||||
}
|
||||
|
||||
WindowsCaptureDevice *winCamera;
|
||||
WindowsCaptureDevice *winMic;
|
||||
|
||||
// TODO: Add more formats, but need some tests.
|
||||
VideoFormatTransform g_VideoFormats[] =
|
||||
@ -97,19 +96,28 @@ VideoFormatTransform g_VideoFormats[] =
|
||||
{ MFVideoFormat_NV12, AV_PIX_FMT_NV12 }
|
||||
};
|
||||
|
||||
const int g_cVideoFormats = 4;
|
||||
AudioFormatTransform g_AudioFormats[] = {
|
||||
{ MFAudioFormat_PCM, 8, AV_SAMPLE_FMT_U8 },
|
||||
{ MFAudioFormat_PCM, 16, AV_SAMPLE_FMT_S16 },
|
||||
{ MFAudioFormat_PCM, 32, AV_SAMPLE_FMT_S32 },
|
||||
{ MFAudioFormat_Float, 32, AV_SAMPLE_FMT_FLT }
|
||||
};
|
||||
|
||||
MediaParam defaultVideoParam = { 640, 480, 0, MFVideoFormat_RGB24 };
|
||||
MediaParam defaultAudioParam = { 44100, 2, 0, MFAudioFormat_PCM };
|
||||
const int g_cVideoFormats = ARRAYSIZE(g_VideoFormats);
|
||||
const int g_cAudioFormats = ARRAYSIZE(g_AudioFormats);
|
||||
|
||||
MediaParam defaultVideoParam = { 640, 480, 0, MFVideoFormat_RGB24 };
|
||||
MediaParam defaultAudioParam = { 44100, 2, 16, MFAudioFormat_PCM };
|
||||
|
||||
HRESULT GetDefaultStride(IMFMediaType *pType, LONG *plStride);
|
||||
|
||||
ReaderCallback::ReaderCallback(WindowsCaptureDevice *device): img_convert_ctx(nullptr){
|
||||
ReaderCallback::ReaderCallback(WindowsCaptureDevice *device): img_convert_ctx(nullptr), resample_ctx(nullptr){
|
||||
this->device = device;
|
||||
}
|
||||
|
||||
ReaderCallback::~ReaderCallback() {
|
||||
sws_freeContext(img_convert_ctx);
|
||||
swr_free(&resample_ctx);
|
||||
}
|
||||
|
||||
HRESULT ReaderCallback::QueryInterface(REFIID riid, void** ppv)
|
||||
@ -130,7 +138,6 @@ HRESULT ReaderCallback::OnReadSample(
|
||||
IMFSample *pSample) {
|
||||
HRESULT hr = S_OK;
|
||||
IMFMediaBuffer *pBuffer = nullptr;
|
||||
LONG lStride = 0;
|
||||
std::lock_guard<std::mutex> lock(device->sdMutex);
|
||||
if (device->isShutDown())
|
||||
return hr;
|
||||
@ -151,6 +158,7 @@ HRESULT ReaderCallback::OnReadSample(
|
||||
int imgJpegSize = device->imgJpegSize;
|
||||
unsigned char* invertedSrcImg = nullptr;
|
||||
LONG srcPadding = 0;
|
||||
LONG lStride = 0;
|
||||
|
||||
UINT32 srcW = device->deviceParam.width;
|
||||
UINT32 srcH = device->deviceParam.height;
|
||||
@ -207,14 +215,53 @@ HRESULT ReaderCallback::OnReadSample(
|
||||
nullptr
|
||||
);
|
||||
}
|
||||
|
||||
delete videoBuffer;
|
||||
break;
|
||||
}
|
||||
case CAPTUREDEVIDE_TYPE::AUDIO:
|
||||
// TODO:
|
||||
case CAPTUREDEVIDE_TYPE::AUDIO: {
|
||||
BYTE *sampleBuf = nullptr;
|
||||
DWORD length = 0;
|
||||
u32 sizeAfterResample = 0;
|
||||
// pSample can be null, in this case ReadSample still should be called to request next frame.
|
||||
if (pSample && Microphone::isNeedInput()) {
|
||||
pBuffer->Lock(&sampleBuf, nullptr, &length);
|
||||
if (!device->rawAudioBuf) {
|
||||
device->rawAudioBuf = new QueueBuf(length * 2); // Alloc enough space.
|
||||
}
|
||||
device->rawAudioBuf->push(sampleBuf, length);
|
||||
if (device->needResample()) {
|
||||
sizeAfterResample = device->rawAudioBuf->getAvailableSize() * device->targetMediaParam.sampleRate / device->deviceParam.sampleRate / device->deviceParam.channels;
|
||||
// Wait until have enough audio data.
|
||||
if (sizeAfterResample + Microphone::availableAudioBufSize() >= Microphone::numNeedSamples() * 2) {
|
||||
u32 rawAudioBufSize = device->rawAudioBuf->getAvailableSize();
|
||||
u8 *tempbuf = new u8[rawAudioBufSize];
|
||||
device->rawAudioBuf->pop(tempbuf, rawAudioBufSize);
|
||||
sizeAfterResample = doResample(
|
||||
&device->resampleBuf, device->targetMediaParam.sampleRate, device->targetMediaParam.channels, &device->resampleBufSize,
|
||||
tempbuf, device->deviceParam.sampleRate, device->deviceParam.channels, device->deviceParam.audioFormat, rawAudioBufSize, device->deviceParam.bitsPerSample);
|
||||
delete[] tempbuf;
|
||||
if (device->resampleBuf)
|
||||
Microphone::addAudioData(device->resampleBuf, sizeAfterResample);
|
||||
}
|
||||
} else {
|
||||
Microphone::addAudioData(sampleBuf, length);
|
||||
}
|
||||
pBuffer->Unlock();
|
||||
}
|
||||
// Request the next frame.
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = device->m_pReader->ReadSample(
|
||||
(DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SafeRelease(&pBuffer);
|
||||
@ -229,6 +276,14 @@ AVPixelFormat ReaderCallback::getAVVideoFormatbyMFVideoFormat(const GUID &MFVide
|
||||
return AV_PIX_FMT_RGB24;
|
||||
}
|
||||
|
||||
AVSampleFormat ReaderCallback::getAVAudioFormatbyMFAudioFormat(const GUID &MFAudioFormat, const u32 &bitsPerSample) {
|
||||
for (int i = 0; i < g_cAudioFormats; i++) {
|
||||
if (MFAudioFormat == g_AudioFormats[i].MFAudioFormat && bitsPerSample == g_AudioFormats[i].bitsPerSample)
|
||||
return g_AudioFormats[i].AVAudioFormat;
|
||||
}
|
||||
return AV_SAMPLE_FMT_S16;
|
||||
}
|
||||
|
||||
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,
|
||||
@ -237,9 +292,9 @@ void ReaderCallback::imgConvert(
|
||||
unsigned char *pSrc[4];
|
||||
unsigned char *pDst[4];
|
||||
|
||||
AVPixelFormat srcAvFormat = getAVVideoFormatbyMFVideoFormat(srcFormat);
|
||||
AVPixelFormat srcAVFormat = getAVVideoFormatbyMFVideoFormat(srcFormat);
|
||||
|
||||
av_image_fill_linesizes(srcLineSizes, srcAvFormat, srcW);
|
||||
av_image_fill_linesizes(srcLineSizes, srcAVFormat, srcW);
|
||||
|
||||
// Is this correct?
|
||||
if (srcPadding != 0) {
|
||||
@ -249,16 +304,14 @@ void ReaderCallback::imgConvert(
|
||||
}
|
||||
}
|
||||
|
||||
av_image_fill_pointers(pSrc, srcAvFormat, srcH, src, srcLineSizes);
|
||||
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,
|
||||
srcAVFormat,
|
||||
dstW,
|
||||
dstH,
|
||||
AV_PIX_FMT_RGB24,
|
||||
@ -348,28 +401,68 @@ void ReaderCallback::imgInvertNV12(unsigned char *dst, int &dstStride, unsigned
|
||||
}
|
||||
}
|
||||
|
||||
u32 ReaderCallback::doResample(u8 **dst, u32 &dstSampleRate, u32 &dstChannels, u32 *dstSize, u8 *src, const u32 &srcSampleRate, const u32 &srcChannels, const GUID &srcFormat, const u32& srcSize, const u32& srcBitsPerSample) {
|
||||
AVSampleFormat srcAVFormat = getAVAudioFormatbyMFAudioFormat(srcFormat, srcBitsPerSample);
|
||||
int outSamplesCount = 0;
|
||||
if (resample_ctx == nullptr) {
|
||||
resample_ctx = swr_alloc_set_opts(nullptr,
|
||||
av_get_default_channel_layout(dstChannels),
|
||||
AV_SAMPLE_FMT_S16,
|
||||
dstSampleRate,
|
||||
av_get_default_channel_layout(srcChannels),
|
||||
srcAVFormat,
|
||||
srcSampleRate,
|
||||
0,
|
||||
nullptr);
|
||||
if (resample_ctx == nullptr || swr_init(resample_ctx) < 0) {
|
||||
swr_free(&resample_ctx);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
int srcSamplesCount = srcSize / srcChannels / av_get_bytes_per_sample(srcAVFormat); // per channel.
|
||||
int outCount = srcSamplesCount * dstSampleRate / srcSampleRate + 256;
|
||||
unsigned int outSize = av_samples_get_buffer_size(nullptr, dstChannels, outCount, AV_SAMPLE_FMT_S16, 0);
|
||||
|
||||
if (!*dst) {
|
||||
*dst = (u8 *)av_malloc(outSize);
|
||||
*dstSize = outSize;
|
||||
}
|
||||
if (!*dst)
|
||||
return 0;
|
||||
|
||||
if(*dstSize < outSize)
|
||||
av_fast_malloc(dst, dstSize, outSize);
|
||||
|
||||
outSamplesCount = swr_convert(resample_ctx, dst, outCount, (const uint8_t **)&src, srcSamplesCount);
|
||||
if (outSamplesCount < 0)
|
||||
return 0;
|
||||
return av_samples_get_buffer_size(nullptr, dstChannels, outSamplesCount, AV_SAMPLE_FMT_S16, 0);
|
||||
}
|
||||
|
||||
WindowsCaptureDevice::WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type) :
|
||||
type(type),
|
||||
m_pCallback(nullptr),
|
||||
m_pSource(nullptr),
|
||||
m_pReader(nullptr),
|
||||
imageRGB(nullptr),
|
||||
imageJpeg(nullptr),
|
||||
imgJpegSize(0),
|
||||
resampleBuf(nullptr),
|
||||
resampleBufSize(0),
|
||||
rawAudioBuf(nullptr),
|
||||
error(CAPTUREDEVIDE_ERROR_NO_ERROR),
|
||||
errorMessage(""),
|
||||
isDeviceChanged(false),
|
||||
state(CAPTUREDEVIDE_STATE::UNINITIALIZED) {
|
||||
param = { 0 };
|
||||
deviceParam = { 0 };
|
||||
|
||||
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));
|
||||
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;
|
||||
}
|
||||
|
||||
@ -380,11 +473,12 @@ WindowsCaptureDevice::WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type) :
|
||||
WindowsCaptureDevice::~WindowsCaptureDevice() {
|
||||
switch (type) {
|
||||
case CAPTUREDEVIDE_TYPE::VIDEO:
|
||||
av_free(imageRGB);
|
||||
av_free(imageJpeg);
|
||||
av_freep(&imageRGB);
|
||||
av_freep(&imageJpeg);
|
||||
break;
|
||||
case CAPTUREDEVIDE_TYPE::AUDIO:
|
||||
// TODO:
|
||||
av_freep(&resampleBuf);
|
||||
delete rawAudioBuf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -412,7 +506,7 @@ bool WindowsCaptureDevice::init() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WindowsCaptureDevice::start(UINT32 width, UINT32 height) {
|
||||
bool WindowsCaptureDevice::start(void *startParam) {
|
||||
HRESULT hr = S_OK;
|
||||
IMFAttributes *pAttributes = nullptr;
|
||||
IMFMediaType *pType = nullptr;
|
||||
@ -434,16 +528,14 @@ bool WindowsCaptureDevice::start(UINT32 width, UINT32 height) {
|
||||
return false;
|
||||
}
|
||||
|
||||
targetMediaParam.width = width;
|
||||
targetMediaParam.height = height;
|
||||
av_image_fill_linesizes(imgRGBLineSizes, AV_PIX_FMT_RGB24, targetMediaParam.width);
|
||||
|
||||
m_pCallback = new ReaderCallback(this);
|
||||
|
||||
std::string selectedDeviceName = type == CAPTUREDEVIDE_TYPE::VIDEO ? g_Config.sCameraDevice : g_Config.sMicDevice;
|
||||
|
||||
switch (state) {
|
||||
case CAPTUREDEVIDE_STATE::STOPPED:
|
||||
for (auto &name : deviceList) {
|
||||
if (name == g_Config.sCameraDevice) {
|
||||
if (name == selectedDeviceName) {
|
||||
selection = count;
|
||||
break;
|
||||
}
|
||||
@ -477,7 +569,21 @@ bool WindowsCaptureDevice::start(UINT32 width, UINT32 height) {
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
switch (type) {
|
||||
case CAPTUREDEVIDE_TYPE::VIDEO:
|
||||
case CAPTUREDEVIDE_TYPE::VIDEO: {
|
||||
if (startParam) {
|
||||
std::vector<int> *resolution = static_cast<std::vector<int>*>(startParam);
|
||||
targetMediaParam.width = resolution->at(0);
|
||||
targetMediaParam.height = resolution->at(1);
|
||||
delete resolution;
|
||||
}
|
||||
|
||||
av_freep(&imageRGB);
|
||||
av_freep(&imageJpeg);
|
||||
imageRGB = (unsigned char*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_RGB24, targetMediaParam.width, targetMediaParam.height, 1));
|
||||
imgJpegSize = av_image_get_buffer_size(AV_PIX_FMT_YUVJ411P, targetMediaParam.width, targetMediaParam.height, 1);
|
||||
imageJpeg = (unsigned char*)av_malloc(imgJpegSize);
|
||||
av_image_fill_linesizes(imgRGBLineSizes, AV_PIX_FMT_RGB24, targetMediaParam.width);
|
||||
|
||||
for (DWORD i = 0; ; i++) {
|
||||
hr = m_pReader->GetNativeMediaType(
|
||||
(DWORD)MF_SOURCE_READER_FIRST_VIDEO_STREAM,
|
||||
@ -512,13 +618,45 @@ bool WindowsCaptureDevice::start(UINT32 width, UINT32 height) {
|
||||
nullptr
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case CAPTUREDEVIDE_TYPE::AUDIO:
|
||||
// TODO:
|
||||
case CAPTUREDEVIDE_TYPE::AUDIO: {
|
||||
if (startParam) {
|
||||
std::vector<u32> *micParam = static_cast<std::vector<u32>*>(startParam);
|
||||
targetMediaParam.sampleRate = micParam->at(0);
|
||||
targetMediaParam.channels = micParam->at(1);
|
||||
delete micParam;
|
||||
}
|
||||
|
||||
for (DWORD i = 0; ; i++) {
|
||||
hr = m_pReader->GetNativeMediaType(
|
||||
(DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,
|
||||
i,
|
||||
&pType
|
||||
);
|
||||
|
||||
if (FAILED(hr)) { break; }
|
||||
|
||||
hr = setDeviceParam(pType);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
break;
|
||||
}
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
hr = m_pReader->ReadSample(
|
||||
(DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,
|
||||
0,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (FAILED(hr)) {
|
||||
@ -597,13 +735,13 @@ std::vector<std::string> WindowsCaptureDevice::getDeviceList(bool forceEnum, int
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
// Get the size needed first
|
||||
dwMinSize = WideCharToMultiByte(CP_OEMCP, NULL, pwstrName, -1, nullptr, 0, nullptr, FALSE);
|
||||
dwMinSize = WideCharToMultiByte(CP_UTF8, 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);
|
||||
WideCharToMultiByte(CP_UTF8, NULL, pwstrName, -1, cstrName, dwMinSize, NULL, FALSE);
|
||||
strName = cstrName;
|
||||
delete[] cstrName;
|
||||
|
||||
@ -670,7 +808,46 @@ HRESULT WindowsCaptureDevice::setDeviceParam(IMFMediaType *pType) {
|
||||
|
||||
break;
|
||||
case CAPTUREDEVIDE_TYPE::AUDIO:
|
||||
// TODO:
|
||||
hr = pType->GetGUID(MF_MT_SUBTYPE, &subtype);
|
||||
if (FAILED(hr))
|
||||
break;
|
||||
|
||||
for (int i = 0; i < g_cVideoFormats; i++) {
|
||||
if (subtype == g_AudioFormats[i].MFAudioFormat) {
|
||||
deviceParam.audioFormat = subtype;
|
||||
getFormat = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!getFormat) {
|
||||
for (int i = 0; i < g_cAudioFormats; i++) {
|
||||
hr = pType->SetGUID(MF_MT_SUBTYPE, g_AudioFormats[i].MFAudioFormat);
|
||||
if (FAILED(hr))
|
||||
continue;
|
||||
|
||||
hr = m_pReader->SetCurrentMediaType(
|
||||
(DWORD)MF_SOURCE_READER_FIRST_AUDIO_STREAM,
|
||||
NULL,
|
||||
pType
|
||||
);
|
||||
|
||||
if (SUCCEEDED(hr)) {
|
||||
deviceParam.audioFormat = g_AudioFormats[i].MFAudioFormat;
|
||||
getFormat = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (SUCCEEDED(hr))
|
||||
hr = pType->GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND, &deviceParam.sampleRate);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
hr = pType->GetUINT32(MF_MT_AUDIO_NUM_CHANNELS, &deviceParam.channels);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
hr = pType->GetUINT32(MF_MT_AUDIO_BITS_PER_SAMPLE, (UINT32 *)&deviceParam.bitsPerSample);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@ -701,10 +878,11 @@ void WindowsCaptureDevice::messageHandler() {
|
||||
CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
MFStartup(MF_VERSION);
|
||||
CAPTUREDEVIDE_MESSAGE message;
|
||||
std::vector<int>* resolution;
|
||||
|
||||
if (type == CAPTUREDEVIDE_TYPE::VIDEO) {
|
||||
setCurrentThreadName("Camera");
|
||||
} else if (type == CAPTUREDEVIDE_TYPE::AUDIO) {
|
||||
setCurrentThreadName("Microphone");
|
||||
}
|
||||
|
||||
while ((message = getMessage()).command != CAPTUREDEVIDE_COMMAND::SHUTDOWN) {
|
||||
@ -713,8 +891,7 @@ void WindowsCaptureDevice::messageHandler() {
|
||||
init();
|
||||
break;
|
||||
case CAPTUREDEVIDE_COMMAND::START:
|
||||
resolution = static_cast<std::vector<int>*>(message.opacity);
|
||||
start(resolution->at(0), resolution->at(1));
|
||||
start(message.opacity);
|
||||
break;
|
||||
case CAPTUREDEVIDE_COMMAND::STOP:
|
||||
stop();
|
||||
@ -781,6 +958,13 @@ HRESULT WindowsCaptureDevice::enumDevices() {
|
||||
return hr;
|
||||
}
|
||||
|
||||
bool WindowsCaptureDevice::needResample() {
|
||||
return deviceParam.sampleRate != targetMediaParam.sampleRate ||
|
||||
deviceParam.channels != targetMediaParam.channels ||
|
||||
deviceParam.audioFormat != targetMediaParam.audioFormat ||
|
||||
deviceParam.bitsPerSample != targetMediaParam.bitsPerSample;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// GetDefaultStride
|
||||
//
|
||||
|
@ -26,10 +26,13 @@
|
||||
#include <queue>
|
||||
#include <thread>
|
||||
|
||||
#include "Core/HLE/sceUsbMic.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#include "libavformat/avformat.h"
|
||||
#include "libswscale/swscale.h"
|
||||
#include "libswresample/swresample.h"
|
||||
#include "libavutil/imgutils.h"
|
||||
}
|
||||
#endif // __cplusplus
|
||||
@ -39,6 +42,12 @@ struct VideoFormatTransform {
|
||||
AVPixelFormat AVVideoFormat;
|
||||
};
|
||||
|
||||
struct AudioFormatTransform {
|
||||
GUID MFAudioFormat;
|
||||
u32 bitsPerSample;
|
||||
AVSampleFormat AVAudioFormat;
|
||||
};
|
||||
|
||||
enum class CAPTUREDEVIDE_TYPE {
|
||||
VIDEO,
|
||||
AUDIO
|
||||
@ -90,8 +99,8 @@ union MediaParam {
|
||||
struct {
|
||||
UINT32 sampleRate;
|
||||
UINT32 channels;
|
||||
LONG padding;
|
||||
GUID audioFomat;
|
||||
LONG bitsPerSample;
|
||||
GUID audioFormat;
|
||||
};
|
||||
};
|
||||
|
||||
@ -127,9 +136,10 @@ public:
|
||||
STDMETHODIMP OnFlush(DWORD) { return S_OK; }
|
||||
|
||||
AVPixelFormat getAVVideoFormatbyMFVideoFormat(const GUID &MFVideoFormat);
|
||||
AVSampleFormat getAVAudioFormatbyMFAudioFormat(const GUID &MFAudioFormat, const u32 &bitsPerSample);
|
||||
|
||||
/*
|
||||
* Always convet the image to RGB24
|
||||
* 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()
|
||||
@ -149,10 +159,20 @@ public:
|
||||
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);
|
||||
|
||||
/*
|
||||
* Always resample to uncompressed signed 16bits
|
||||
* @param dst pointer to pointer of dst buffer could be a nullptr, would be overwritten by a new pointer if space too small or a nullptr, should be freed by av_free/av_freep by caller
|
||||
* @param dstSize pointer to size of the dst buffer, could be modified after this func
|
||||
* @param srcFormat MF_MT_SUBTYPE attribute of the source audio data
|
||||
* @param srcSize size of valid data in the source buffer, in bytes
|
||||
* @return size of output in bytes
|
||||
*/
|
||||
u32 doResample(u8 **dst, u32 &dstSampleRate, u32 &dstChannels, u32 *dstSize, u8 *src, const u32 &srcSampleRate, const u32 &srcChannels, const GUID &srcFormat, const u32 &srcSize, const u32& srcBitsPerSamples);
|
||||
|
||||
protected:
|
||||
WindowsCaptureDevice *device;
|
||||
SwsContext *img_convert_ctx;
|
||||
SwrContext *resample_ctx;
|
||||
};
|
||||
|
||||
class WindowsCaptureDevice {
|
||||
@ -160,10 +180,10 @@ public:
|
||||
WindowsCaptureDevice(CAPTUREDEVIDE_TYPE type);
|
||||
~WindowsCaptureDevice();
|
||||
|
||||
static void CheckDevices();
|
||||
void CheckDevices();
|
||||
|
||||
bool init();
|
||||
bool start(UINT32 width, UINT32 height);
|
||||
bool start(void *startParam);
|
||||
bool stop();
|
||||
|
||||
CAPTUREDEVIDE_ERROR getError() const { return error; }
|
||||
@ -178,12 +198,15 @@ public:
|
||||
HRESULT setDeviceParam(IMFMediaType *pType);
|
||||
|
||||
bool isShutDown() const { return state == CAPTUREDEVIDE_STATE::SHUTDOWN; }
|
||||
bool isStarted() const { return state == CAPTUREDEVIDE_STATE::STARTED; }
|
||||
|
||||
void sendMessage(CAPTUREDEVIDE_MESSAGE message);
|
||||
CAPTUREDEVIDE_MESSAGE getMessage();
|
||||
|
||||
HRESULT enumDevices();
|
||||
|
||||
bool needResample();
|
||||
|
||||
friend class ReaderCallback;
|
||||
|
||||
protected:
|
||||
@ -199,6 +222,8 @@ protected:
|
||||
CAPTUREDEVIDE_ERROR error;
|
||||
std::string errorMessage;
|
||||
|
||||
bool isDeviceChanged;
|
||||
|
||||
// MF interface.
|
||||
ReaderCallback *m_pCallback;
|
||||
IMFSourceReader *m_pReader;
|
||||
@ -220,6 +245,12 @@ protected:
|
||||
int imgRGBLineSizes[4];
|
||||
unsigned char *imageJpeg;
|
||||
int imgJpegSize;
|
||||
|
||||
//Microphone only
|
||||
u8 *resampleBuf;
|
||||
u32 resampleBufSize;
|
||||
QueueBuf *rawAudioBuf;
|
||||
};
|
||||
|
||||
extern WindowsCaptureDevice *winCamera;
|
||||
extern WindowsCaptureDevice *winMic;
|
||||
|
@ -849,7 +849,10 @@ namespace MainWindow
|
||||
#ifndef _M_ARM
|
||||
DinputDevice::CheckDevices();
|
||||
#endif
|
||||
WindowsCaptureDevice::CheckDevices();
|
||||
if (winCamera)
|
||||
winCamera->CheckDevices();
|
||||
if (winMic)
|
||||
winMic->CheckDevices();
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
|
||||
case WM_VERYSLEEPY_MSG:
|
||||
|
Loading…
Reference in New Issue
Block a user