Skip to content

Commit

Permalink
Merge pull request #12580 from Florin9doi/v4l2b
Browse files Browse the repository at this point in the history
Add camera support for linux (V4L2)
  • Loading branch information
hrydgard authored Jan 16, 2020
2 parents d79abaa + 09e3222 commit 7db5471
Show file tree
Hide file tree
Showing 13 changed files with 647 additions and 84 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1668,6 +1668,8 @@ add_library(${CoreLibName} ${CoreLinkType}
Core/HW/SimpleAudioDec.h
Core/HW/AsyncIOManager.cpp
Core/HW/AsyncIOManager.h
Core/HW/Camera.cpp
Core/HW/Camera.h
Core/HW/MediaEngine.cpp
Core/HW/MediaEngine.h
Core/HW/MpegDemux.cpp
Expand Down
4 changes: 2 additions & 2 deletions Core/Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -707,8 +707,8 @@ static ConfigSetting graphicsSettings[] = {
#ifdef _WIN32
ConfigSetting("D3D11Device", &g_Config.sD3D11Device, "", true, false),
#endif
#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
ConfigSetting("WinCameraDevice", &g_Config.sWinCameraDevice, "", true, false),
#if (defined(_WIN32) && !PPSSPP_PLATFORM(UWP)) || PPSSPP_PLATFORM(LINUX)
ConfigSetting("CameraDevice", &g_Config.sCameraDevice, "", true, false),
#endif
ConfigSetting("VendorBugChecksEnabled", &g_Config.bVendorBugChecksEnabled, true, false, false),
ReportedConfigSetting("RenderingMode", &g_Config.iRenderingMode, 1, true, true),
Expand Down
2 changes: 1 addition & 1 deletion Core/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +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
std::string sCameraDevice;

bool bSoftwareRendering;
bool bHardwareTransform; // only used in the GLES backend
Expand Down
2 changes: 2 additions & 0 deletions Core/Core.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@
<ClCompile Include="HLE\sceUsbAcc.cpp" />
<ClCompile Include="HLE\sceUsbCam.cpp" />
<ClCompile Include="HLE\sceUsbMic.cpp" />
<ClCompile Include="HW\Camera.cpp" />
<ClCompile Include="MIPS\IR\IRAsm.cpp" />
<ClCompile Include="MIPS\IR\IRCompALU.cpp" />
<ClCompile Include="MIPS\IR\IRCompBranch.cpp" />
Expand Down Expand Up @@ -903,6 +904,7 @@
<ClInclude Include="HLE\sceUsbAcc.h" />
<ClInclude Include="HLE\sceUsbCam.h" />
<ClInclude Include="HLE\sceUsbMic.h" />
<ClInclude Include="HW\Camera.h" />
<ClInclude Include="MIPS\IR\IRFrontend.h" />
<ClInclude Include="MIPS\IR\IRInst.h" />
<ClInclude Include="MIPS\IR\IRInterpreter.h" />
Expand Down
6 changes: 6 additions & 0 deletions Core/Core.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -740,6 +740,9 @@
<ClCompile Include="HLE\sceUsbMic.cpp">
<Filter>HLE\Libraries</Filter>
</ClCompile>
<ClCompile Include="HW\Camera.cpp">
<Filter>HW</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="ELF\ElfReader.h">
Expand Down Expand Up @@ -1373,6 +1376,9 @@
<ClInclude Include="HLE\sceUsbMic.h">
<Filter>HLE\Libraries</Filter>
</ClInclude>
<ClInclude Include="HW\Camera.h">
<Filter>HW</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="CMakeLists.txt" />
Expand Down
14 changes: 12 additions & 2 deletions Core/HLE/sceUsb.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,16 @@ static int sceUsbDeactivate(u32 pid) {
return 0;
}

static int sceUsbWaitState(int state, int waitMode, u32 timeoutAddr) {
ERROR_LOG(HLE, "UNIMPL sceUsbWaitStat(%i, %i, %08x)", state, waitMode, timeoutAddr);
return sceUsbGetState();
}

static int sceUsbWaitStateCB(int state, int waitMode, u32 timeoutAddr) {
ERROR_LOG(HLE, "UNIMPL sceUsbWaitStateCB(%i, %i, %08x)", state, waitMode, timeoutAddr);
return 0;
}

const HLEFunction sceUsb[] =
{
{0XAE5DE6AF, &WrapI_CUU<sceUsbStart>, "sceUsbStart", 'i', "sxx"},
Expand All @@ -108,8 +118,8 @@ const HLEFunction sceUsb[] =
{0X112CC951, nullptr, "sceUsbGetDrvState", '?', "" },
{0X586DB82C, &WrapI_U<sceUsbActivate>, "sceUsbActivate", 'i', "x" },
{0XC572A9C8, &WrapI_U<sceUsbDeactivate>, "sceUsbDeactivate", 'i', "x" },
{0X5BE0E002, nullptr, "sceUsbWaitState", '?', "" },
{0X616F2B61, nullptr, "sceUsbWaitStateCB", '?', "" },
{0X5BE0E002, &WrapI_IIU<sceUsbWaitState>, "sceUsbWaitState", '?', "xxx"},
{0X616F2B61, &WrapI_IIU<sceUsbWaitStateCB>, "sceUsbWaitStateCB", '?', "xxx"},
{0X1C360735, nullptr, "sceUsbWaitCancel", '?', "" },
};

Expand Down
214 changes: 155 additions & 59 deletions Core/HLE/sceUsbCam.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,55 +19,95 @@
#include <mutex>

#include "base/NativeApp.h"
#include "ppsspp_config.h"
#include "Common/ChunkFile.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/FunctionWrappers.h"
#include "Core/HLE/sceUsbCam.h"
#include "Core/HW/Camera.h"
#include "Core/MemMapHelpers.h"

#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include "Windows/CaptureDevice.h"
#undef min
#endif


PspUsbCamSetupMicParam micParam;
PspUsbCamSetupVideoParam videoParam;
Camera::Config *config;

unsigned int videoBufferLength = 0;
unsigned int nextVideoFrame = 0;
uint8_t *videoBuffer;
std::mutex videoBufferMutex;
bool isShutDown = false;

enum {
VIDEO_BUFFER_SIZE = 40 * 1000,
};

void __UsbCamInit() {
videoBuffer = new uint8_t[VIDEO_BUFFER_SIZE];
isShutDown = false;
config = new Camera::Config;
config->mode = Camera::Mode::Unused;
config->type = Camera::ConfigType::CfNone;
videoBuffer = new uint8_t[VIDEO_BUFFER_SIZE];
}

void __UsbCamShutdown() {
#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
if (winCamera) {
winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr });
}
#endif
isShutDown = true;
void __UsbCamDoState(PointerWrap &p) {
auto s = p.Section("sceUsbCam", 0, 1);
if (!s) {
return;
}

p.Do(*config);
if (config->mode == Camera::Mode::Video) { // stillImage? TBD
Camera::startCapture();
}
}

void __UsbCamShutdown() {
Camera::stopCapture();
delete[] videoBuffer;
videoBuffer = nullptr;
delete[] config;
config = nullptr;
}

// TODO: Technically, we should store the videoBuffer into the savestate, if this
// module has been initialized.

static int getCameraResolution(Camera::ConfigType type, int *width, int *height) {
if (type == Camera::ConfigType::CfStill || type == Camera::ConfigType::CfVideo) {
switch(config->stillParam.resolution) {
case 0: *width = 160; *height = 120; return 0;
case 1: *width = 176; *height = 144; return 0;
case 2: *width = 320; *height = 240; return 0;
case 3: *width = 352; *height = 288; return 0;
case 4: *width = 640; *height = 480; return 0;
case 5: *width =1024; *height = 768; return 0;
case 6: *width =1280; *height = 960; return 0;
case 7: *width = 480; *height = 272; return 0;
case 8: *width = 360; *height = 272; return 0;
}
} else if (type == Camera::ConfigType::CfStillEx || type == Camera::ConfigType::CfVideoEx) {
switch(config->stillExParam.resolution) {
case 0: *width = 160; *height = 120; return 0;
case 1: *width = 176; *height = 144; return 0;
case 2: *width = 320; *height = 240; return 0;
case 3: *width = 352; *height = 288; return 0;
case 4: *width = 360; *height = 272; return 0;
case 5: *width = 480; *height = 272; return 0;
case 6: *width = 640; *height = 480; return 0;
case 7: *width =1024; *height = 768; return 0;
case 8: *width =1280; *height = 960; return 0;
}
}
*width = 0; *height = 0; return 1;
}


static int sceUsbCamSetupMic(u32 paramAddr, u32 workareaAddr, int wasize) {
INFO_LOG(HLE, "UNIMPL sceUsbCamSetupMic");
if (Memory::IsValidRange(paramAddr, sizeof(micParam))) {
Memory::ReadStruct(paramAddr, &micParam);
if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupMicParam))) {
Memory::ReadStruct(paramAddr, &config->micParam);
}
return 0;
}
Expand All @@ -89,59 +129,37 @@ static int sceUsbCamReadMicBlocking(u32 bufAddr, u32 size) {
Memory::Write_U8(i & 0xFF, bufAddr + i);
}
}
hleEatMicro(1000000 / micParam.frequency * (size / 2));
hleEatMicro(1000000 / config->micParam.frequency * (size / 2));
return size;
}

static int sceUsbCamSetupVideo(u32 paramAddr, u32 workareaAddr, int wasize) {
INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo");
if (Memory::IsValidRange(paramAddr, sizeof(videoParam))) {
Memory::ReadStruct(paramAddr, &videoParam);
if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupVideoParam))) {
Memory::ReadStruct(paramAddr, &config->videoParam);
}

INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - size: %d", videoParam.size);
INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - resolution: %d", videoParam.resolution);
INFO_LOG(HLE, "UNIMPL sceUsbCamSetupVideo - framesize: %d", videoParam.framesize);

std::lock_guard<std::mutex> lock(videoBufferMutex);
videoBufferLength = sizeof(sceUsbCamDummyImage);
memset(videoBuffer, 0, VIDEO_BUFFER_SIZE);
memcpy(videoBuffer, sceUsbCamDummyImage, sizeof(sceUsbCamDummyImage));
config->type = Camera::ConfigType::CfVideo;
return 0;
}

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 });
static int sceUsbCamSetupVideoEx(u32 paramAddr, u32 workareaAddr, int wasize) {
if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupVideoExParam))) {
Memory::ReadStruct(paramAddr, &config->videoExParam);
}

#else
System_SendMessage("camera_command", "startVideo");
#endif
config->type = Camera::ConfigType::CfVideoEx;
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
static int sceUsbCamStartVideo() {
std::lock_guard<std::mutex> lock(videoBufferMutex);
videoBufferLength = sizeof(sceUsbCamDummyImage);
memset(videoBuffer, 0, VIDEO_BUFFER_SIZE);
memcpy(videoBuffer, sceUsbCamDummyImage, sizeof(sceUsbCamDummyImage));
Camera::startCapture();
return 0;
}

static int sceUsbCamAutoImageReverseSW(int rev) {
INFO_LOG(HLE, "UNIMPL sceUsbCamAutoImageReverseSW");
static int sceUsbCamStopVideo() {
Camera::stopCapture();
return 0;
}

Expand Down Expand Up @@ -169,6 +187,29 @@ static int sceUsbCamPollReadVideoFrameEnd() {
return nextVideoFrame;
}

static int sceUsbCamSetupStill(u32 paramAddr) {
INFO_LOG(HLE, "UNIMPL sceUsbCamSetupStill");
if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupStillParam))) {
Memory::ReadStruct(paramAddr, &config->stillParam);
}
config->type = Camera::ConfigType::CfStill;
return 0;
}

static int sceUsbCamSetupStillEx(u32 paramAddr) {
INFO_LOG(HLE, "UNIMPL sceUsbCamSetupStillEx");
if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupStillExParam))) {
Memory::ReadStruct(paramAddr, &config->stillExParam);
}
config->type = Camera::ConfigType::CfStillEx;
return 0;
}

static int sceUsbCamAutoImageReverseSW(int rev) {
INFO_LOG(HLE, "UNIMPL sceUsbCamAutoImageReverseSW");
return 0;
}

const HLEFunction sceUsbCam[] =
{
{ 0X03ED7A82, &WrapI_UUI<sceUsbCamSetupMic>, "sceUsbCamSetupMic", 'i', "xxi" },
Expand All @@ -183,7 +224,7 @@ const HLEFunction sceUsbCam[] =
{ 0X08AEE98A, nullptr, "sceUsbCamSetMicGain", '?', "" },

{ 0X17F7B2FB, &WrapI_UUI<sceUsbCamSetupVideo>, "sceUsbCamSetupVideo", 'i', "xxi" },
{ 0XCFE9E999, nullptr, "sceUsbCamSetupVideoEx", '?', "" },
{ 0XCFE9E999, &WrapI_UUI<sceUsbCamSetupVideoEx>, "sceUsbCamSetupVideoEx", 'i', "xxi" },
{ 0X574A8C3F, &WrapI_V<sceUsbCamStartVideo>, "sceUsbCamStartVideo", 'i', "" },
{ 0X6CF32CB9, &WrapI_V<sceUsbCamStopVideo>, "sceUsbCamStopVideo", 'i', "" },
{ 0X7DAC0C71, &WrapI_UU<sceUsbCamReadVideoFrameBlocking>, "sceUsbCamReadVideoFrameBlocking", 'i', "xx" },
Expand All @@ -192,8 +233,8 @@ const HLEFunction sceUsbCam[] =
{ 0X41E73E95, &WrapI_V<sceUsbCamPollReadVideoFrameEnd>, "sceUsbCamPollReadVideoFrameEnd", 'i', "" },
{ 0XDF9D0C92, nullptr, "sceUsbCamGetReadVideoFrameSize", '?', "" },

{ 0X3F0CF289, nullptr, "sceUsbCamSetupStill", '?', "" },
{ 0X0A41A298, nullptr, "sceUsbCamSetupStillEx", '?', "" },
{ 0X3F0CF289, &WrapI_U<sceUsbCamSetupStill>, "sceUsbCamSetupStill", 'i', "x" },
{ 0X0A41A298, &WrapI_U<sceUsbCamSetupStillEx>, "sceUsbCamSetupStillEx", 'i', "x" },
{ 0X61BE5CAC, nullptr, "sceUsbCamStillInputBlocking", '?', "" },
{ 0XFB0A6C5D, nullptr, "sceUsbCamStillInput", '?', "" },
{ 0X7563AFA1, nullptr, "sceUsbCamStillWaitInputEnd", '?', "" },
Expand Down Expand Up @@ -233,10 +274,65 @@ void Register_sceUsbCam()
RegisterModule("sceUsbCam", ARRAY_SIZE(sceUsbCam), sceUsbCam);
}

std::vector<std::string> Camera::getDeviceList() {
#if PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID)
return __v4l_getDeviceList();
#elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
if (winCamera) {
return winCamera->getDeviceList();
}
#endif
return std::vector<std::string>();
}

int Camera::startCapture() {
int width, height;
getCameraResolution(config->type, &width, &height);
INFO_LOG(HLE, "%s resolution: %dx%d", __FUNCTION__, width, height);

config->mode = Camera::Mode::Video;
#if PPSSPP_PLATFORM(ANDROID)
System_SendMessage("camera_command", "startVideo");
#elif PPSSPP_PLATFORM(LINUX)
__v4l_startCapture(width, height);
#elif 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
ERROR_LOG(HLE, "%s not implemented", __FUNCTION__);
#endif
return 0;
}

int Camera::stopCapture() {
INFO_LOG(HLE, "%s", __FUNCTION__);
#if PPSSPP_PLATFORM(ANDROID)
System_SendMessage("camera_command", "stopVideo");
#elif PPSSPP_PLATFORM(LINUX)
__v4l_stopCapture();
#elif defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
if (winCamera) {
winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::STOP, nullptr });
winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr });
}
#else
ERROR_LOG(HLE, "%s not implemented", __FUNCTION__);
#endif
config->mode = Camera::Mode::Unused;
return 0;
}

void Camera::pushCameraImage(long long length, unsigned char* image) {
std::lock_guard<std::mutex> lock(videoBufferMutex);
if (isShutDown)
if (!videoBuffer) {
return;
}
memset(videoBuffer, 0, VIDEO_BUFFER_SIZE);
if (length > VIDEO_BUFFER_SIZE) {
videoBufferLength = 0;
Expand Down
Loading

0 comments on commit 7db5471

Please sign in to comment.