diff --git a/CMakeLists.txt b/CMakeLists.txt
index df14ac85a6..176a4500e9 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -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
diff --git a/Core/Config.cpp b/Core/Config.cpp
index 3dd37d49c7..0e7a27f655 100644
--- a/Core/Config.cpp
+++ b/Core/Config.cpp
@@ -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),
diff --git a/Core/Config.h b/Core/Config.h
index 3252164101..88be654ea3 100644
--- a/Core/Config.h
+++ b/Core/Config.h
@@ -128,7 +128,7 @@ public:
// 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
diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj
index 22d258ecad..021cbfb153 100644
--- a/Core/Core.vcxproj
+++ b/Core/Core.vcxproj
@@ -375,6 +375,7 @@
+
@@ -903,6 +904,7 @@
+
diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters
index 7f096c1b50..d1257aa716 100644
--- a/Core/Core.vcxproj.filters
+++ b/Core/Core.vcxproj.filters
@@ -740,6 +740,9 @@
HLE\Libraries
+
+ HW
+
@@ -1373,6 +1376,9 @@
HLE\Libraries
+
+ HW
+
diff --git a/Core/HLE/sceUsb.cpp b/Core/HLE/sceUsb.cpp
index 361f55f26b..cd2e5e5e16 100644
--- a/Core/HLE/sceUsb.cpp
+++ b/Core/HLE/sceUsb.cpp
@@ -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", 'i', "sxx"},
@@ -108,8 +118,8 @@ const HLEFunction sceUsb[] =
{0X112CC951, nullptr, "sceUsbGetDrvState", '?', "" },
{0X586DB82C, &WrapI_U, "sceUsbActivate", 'i', "x" },
{0XC572A9C8, &WrapI_U, "sceUsbDeactivate", 'i', "x" },
- {0X5BE0E002, nullptr, "sceUsbWaitState", '?', "" },
- {0X616F2B61, nullptr, "sceUsbWaitStateCB", '?', "" },
+ {0X5BE0E002, &WrapI_IIU, "sceUsbWaitState", '?', "xxx"},
+ {0X616F2B61, &WrapI_IIU, "sceUsbWaitStateCB", '?', "xxx"},
{0X1C360735, nullptr, "sceUsbWaitCancel", '?', "" },
};
diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp
index 50f1147b05..da850ce8f9 100644
--- a/Core/HLE/sceUsbCam.cpp
+++ b/Core/HLE/sceUsbCam.cpp
@@ -19,55 +19,95 @@
#include
#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 __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() {
-#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
- if (winCamera) {
- winCamera->sendMessage({ CAPTUREDEVIDE_COMMAND::SHUTDOWN, nullptr });
- }
-#endif
- isShutDown = true;
-
+ 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;
}
@@ -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);
}
+ config->type = Camera::ConfigType::CfVideo;
+ return 0;
+}
- 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 lock(videoBufferMutex);
- videoBufferLength = sizeof(sceUsbCamDummyImage);
- memset(videoBuffer, 0, VIDEO_BUFFER_SIZE);
- memcpy(videoBuffer, sceUsbCamDummyImage, sizeof(sceUsbCamDummyImage));
+static int sceUsbCamSetupVideoEx(u32 paramAddr, u32 workareaAddr, int wasize) {
+ if (Memory::IsValidRange(paramAddr, sizeof(PspUsbCamSetupVideoExParam))) {
+ Memory::ReadStruct(paramAddr, &config->videoExParam);
+ }
+ config->type = Camera::ConfigType::CfVideoEx;
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 });
- }
-
-#else
- System_SendMessage("camera_command", "startVideo");
-#endif
+ std::lock_guard lock(videoBufferMutex);
+ videoBufferLength = sizeof(sceUsbCamDummyImage);
+ memset(videoBuffer, 0, VIDEO_BUFFER_SIZE);
+ memcpy(videoBuffer, sceUsbCamDummyImage, sizeof(sceUsbCamDummyImage));
+ Camera::startCapture();
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;
-}
-
-static int sceUsbCamAutoImageReverseSW(int rev) {
- INFO_LOG(HLE, "UNIMPL sceUsbCamAutoImageReverseSW");
+ Camera::stopCapture();
return 0;
}
@@ -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", 'i', "xxi" },
@@ -183,7 +224,7 @@ const HLEFunction sceUsbCam[] =
{ 0X08AEE98A, nullptr, "sceUsbCamSetMicGain", '?', "" },
{ 0X17F7B2FB, &WrapI_UUI, "sceUsbCamSetupVideo", 'i', "xxi" },
- { 0XCFE9E999, nullptr, "sceUsbCamSetupVideoEx", '?', "" },
+ { 0XCFE9E999, &WrapI_UUI, "sceUsbCamSetupVideoEx", 'i', "xxi" },
{ 0X574A8C3F, &WrapI_V, "sceUsbCamStartVideo", 'i', "" },
{ 0X6CF32CB9, &WrapI_V, "sceUsbCamStopVideo", 'i', "" },
{ 0X7DAC0C71, &WrapI_UU, "sceUsbCamReadVideoFrameBlocking", 'i', "xx" },
@@ -192,8 +233,8 @@ const HLEFunction sceUsbCam[] =
{ 0X41E73E95, &WrapI_V, "sceUsbCamPollReadVideoFrameEnd", 'i', "" },
{ 0XDF9D0C92, nullptr, "sceUsbCamGetReadVideoFrameSize", '?', "" },
- { 0X3F0CF289, nullptr, "sceUsbCamSetupStill", '?', "" },
- { 0X0A41A298, nullptr, "sceUsbCamSetupStillEx", '?', "" },
+ { 0X3F0CF289, &WrapI_U, "sceUsbCamSetupStill", 'i', "x" },
+ { 0X0A41A298, &WrapI_U, "sceUsbCamSetupStillEx", 'i', "x" },
{ 0X61BE5CAC, nullptr, "sceUsbCamStillInputBlocking", '?', "" },
{ 0XFB0A6C5D, nullptr, "sceUsbCamStillInput", '?', "" },
{ 0X7563AFA1, nullptr, "sceUsbCamStillWaitInputEnd", '?', "" },
@@ -233,10 +274,65 @@ void Register_sceUsbCam()
RegisterModule("sceUsbCam", ARRAY_SIZE(sceUsbCam), sceUsbCam);
}
+std::vector 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();
+}
+
+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 lock(videoBufferMutex);
- if (isShutDown)
+ if (!videoBuffer) {
return;
+ }
memset(videoBuffer, 0, VIDEO_BUFFER_SIZE);
if (length > VIDEO_BUFFER_SIZE) {
videoBufferLength = 0;
diff --git a/Core/HLE/sceUsbCam.h b/Core/HLE/sceUsbCam.h
index ebb5388db6..3d6bfbcf3f 100644
--- a/Core/HLE/sceUsbCam.h
+++ b/Core/HLE/sceUsbCam.h
@@ -17,16 +17,18 @@
#pragma once
+#include
+#include
+#include
+#include "Core/HLE/FunctionWrappers.h"
+
void Register_sceUsbCam();
void __UsbCamInit();
+void __UsbCamDoState(PointerWrap &p);
void __UsbCamShutdown();
-namespace Camera {
- void pushCameraImage(long long length, unsigned char *image);
-}
-
-typedef struct PspUsbCamSetupMicParam {
+typedef struct {
int size;
u32 unk;
int gain;
@@ -34,7 +36,30 @@ typedef struct PspUsbCamSetupMicParam {
int frequency;
} PspUsbCamSetupMicParam;
-typedef struct PspUsbCamSetupVideoParam {
+typedef struct {
+ int size;
+ int resolution;
+ int jpegsize;
+ int reverseflags;
+ int delay;
+ int complevel;
+} PspUsbCamSetupStillParam;
+
+typedef struct {
+ int size;
+ u32 unk;
+ int resolution;
+ int jpegsize;
+ int complevel;
+ u32 unk2;
+ u32 unk3;
+ int flip;
+ int mirror;
+ int delay;
+ u32 unk4[5];
+} PspUsbCamSetupStillExParam;
+
+typedef struct {
int size;
int resolution;
int framerate;
@@ -49,6 +74,54 @@ typedef struct PspUsbCamSetupVideoParam {
int evlevel;
} PspUsbCamSetupVideoParam;
+typedef struct {
+ int size;
+ u32 unk;
+ int resolution;
+ int framerate;
+ u32 unk2;
+ u32 unk3;
+ int wb;
+ int saturation;
+ int brightness;
+ int contrast;
+ int sharpness;
+ u32 unk4;
+ u32 unk5;
+ u32 unk6[3];
+ int effectmode;
+ u32 unk7;
+ u32 unk8;
+ u32 unk9;
+ u32 unk10;
+ u32 unk11;
+ int framesize;
+ u32 unk12;
+ int evlevel;
+} PspUsbCamSetupVideoExParam;
+
+namespace Camera {
+ enum Mode { Unused, Still, Video };
+ enum ConfigType { CfNone, CfStill, CfStillEx, CfVideo, CfVideoEx };
+
+ typedef struct {
+ Mode mode;
+ ConfigType type;
+ PspUsbCamSetupMicParam micParam;
+ union {
+ PspUsbCamSetupStillParam stillParam;
+ PspUsbCamSetupStillExParam stillExParam;
+ PspUsbCamSetupVideoParam videoParam;
+ PspUsbCamSetupVideoExParam videoExParam;
+ };
+ } Config;
+
+ std::vector getDeviceList();
+ int startCapture();
+ int stopCapture();
+ void pushCameraImage(long long length, unsigned char *image);
+}
+
static const unsigned char sceUsbCamDummyImage[] = {
0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46, 0x00, 0x01, 0x01, 0x01, 0x00, 0x60,
0x00, 0x60, 0x00, 0x00, 0xFF, 0xE1, 0x00, 0x68, 0x45, 0x78, 0x69, 0x66, 0x00, 0x00, 0x4D, 0x4D,
diff --git a/Core/HW/Camera.cpp b/Core/HW/Camera.cpp
new file mode 100644
index 0000000000..2260f452e8
--- /dev/null
+++ b/Core/HW/Camera.cpp
@@ -0,0 +1,314 @@
+// 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 "Camera.h"
+
+#if PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID)
+
+std::vector __v4l_getDeviceList() {
+ std::vector deviceList;
+ for (int i = 0; i < 64; i++) {
+ char path[256];
+ snprintf(path, sizeof(path), "/dev/video%d", i);
+ if (access(path, F_OK) < 0) {
+ break;
+ }
+ int fd = -1;
+ if((fd = open(path, O_RDONLY)) < 0) {
+ ERROR_LOG(HLE, "Cannot open '%s'; errno=%d(%s)", path, errno, strerror(errno));
+ continue;
+ }
+ struct v4l2_capability video_cap;
+ if(ioctl(fd, VIDIOC_QUERYCAP, &video_cap) < 0) {
+ ERROR_LOG(HLE, "VIDIOC_QUERYCAP");
+ goto cont;
+ } else {
+ char device[256];
+ snprintf(device, sizeof(device), "%d:%s", i, video_cap.card);
+ deviceList.push_back(device);
+ }
+cont:
+ close(fd);
+ fd = -1;
+ }
+ return deviceList;
+}
+
+void convert_frame(unsigned char *inData, AVPixelFormat inFormat, unsigned char **outData, int *outLen) {
+ // resize
+ uint8_t *src[4] = {0};
+ uint8_t *dst[4] = {0};
+ int srcStride[4], dstStride[4];
+
+ unsigned char *rgbData = (unsigned char*)malloc(v4l_ideal_width * v4l_ideal_height * 3);
+
+ av_image_fill_linesizes(srcStride, inFormat, v4l_hw_width);
+ av_image_fill_linesizes(dstStride, AV_PIX_FMT_RGB24, v4l_ideal_width);
+
+ av_image_fill_pointers(src, inFormat, v4l_hw_height, inData, srcStride);
+ av_image_fill_pointers(dst, AV_PIX_FMT_RGB24, v4l_ideal_height, rgbData, dstStride);
+
+ sws_scale(sws_context,
+ src, srcStride, 0, v4l_height_fixed_aspect,
+ dst, dstStride);
+
+ // compress jpeg
+ *outLen = v4l_ideal_width * v4l_ideal_height * 3;
+ *outData = (unsigned char*)malloc(*outLen);
+
+ jpge::params params;
+ params.m_quality = 60;
+ params.m_subsampling = jpge::H2V2;
+ params.m_two_pass_flag = false;
+ jpge::compress_image_to_jpeg_file_in_memory(
+ *outData, *outLen, v4l_ideal_width, v4l_ideal_height, 3, rgbData, params);
+ free(rgbData);
+}
+
+void *v4l_loop(void *data) {
+ setCurrentThreadName("v4l_loop");
+ while (v4l_fd >= 0) {
+ struct v4l2_buffer buf;
+ memset(&buf, 0, sizeof(buf));
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+
+ if (ioctl(v4l_fd, VIDIOC_DQBUF, &buf) == -1) {
+ ERROR_LOG(HLE, "VIDIOC_DQBUF; errno=%d(%s)", errno, strerror(errno));
+ switch (errno) {
+ case EAGAIN:
+ continue;
+ default:
+ return NULL;
+ }
+ }
+
+ unsigned char *jpegData;
+ int jpegLen;
+
+ if (v4l_format == V4L2_PIX_FMT_YUYV) {
+ convert_frame((unsigned char*)v4l_buffer, AV_PIX_FMT_YUYV422, &jpegData, &jpegLen);
+ } else if (v4l_format == V4L2_PIX_FMT_MJPEG) {
+ // decompress jpeg
+ int width, height, req_comps;
+ unsigned char *rgbData = jpgd::decompress_jpeg_image_from_memory(
+ (unsigned char*)v4l_buffer, buf.bytesused, &width, &height, &req_comps, 3);
+
+ convert_frame(rgbData, AV_PIX_FMT_RGB24, &jpegData, &jpegLen);
+ free(rgbData);
+ }
+
+ Camera::pushCameraImage(jpegLen, jpegData);
+ free(jpegData);
+
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ if (ioctl(v4l_fd, VIDIOC_QBUF, &buf) == -1) {
+ ERROR_LOG(HLE, "VIDIOC_QBUF");
+ return NULL;
+ }
+ }
+}
+
+int __v4l_startCapture(int ideal_width, int ideal_height) {
+ if (v4l_fd >= 0) {
+ __v4l_stopCapture();
+ }
+ v4l_ideal_width = ideal_width;
+ v4l_ideal_height = ideal_height;
+
+ int dev_index = 0;
+ char dev_name[64];
+ sscanf(g_Config.sCameraDevice.c_str(), "%d:", &dev_index);
+ snprintf(dev_name, sizeof(dev_name), "/dev/video%d", dev_index);
+
+ if ((v4l_fd = open(dev_name, O_RDWR)) == -1) {
+ ERROR_LOG(HLE, "Cannot open '%s'; errno=%d(%s)", dev_name, errno, strerror(errno));
+ return -1;
+ }
+
+ struct v4l2_capability cap;
+ memset(&cap, 0, sizeof(cap));
+ if (ioctl(v4l_fd, VIDIOC_QUERYCAP, &cap) == -1) {
+ ERROR_LOG(HLE, "VIDIOC_QUERYCAP");
+ return -1;
+ }
+ if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
+ ERROR_LOG(HLE, "V4L2_CAP_VIDEO_CAPTURE");
+ return -1;
+ }
+ if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
+ ERROR_LOG(HLE, "V4L2_CAP_STREAMING");
+ return -1;
+ }
+
+ struct v4l2_format fmt;
+ memset(&fmt, 0, sizeof(fmt));
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix.pixelformat = 0;
+
+ // select a pixel format
+ struct v4l2_fmtdesc desc;
+ memset(&desc, 0, sizeof(desc));
+ desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ while (ioctl(v4l_fd, VIDIOC_ENUM_FMT, &desc) == 0) {
+ desc.index++;
+ INFO_LOG(HLE, "V4L2: pixel format supported: %s", desc.description);
+ if (fmt.fmt.pix.pixelformat != 0) {
+ continue;
+ } else if (desc.pixelformat == V4L2_PIX_FMT_YUYV
+ || desc.pixelformat == V4L2_PIX_FMT_MJPEG) {
+ INFO_LOG(HLE, "V4L2: %s selected", desc.description);
+ fmt.fmt.pix.pixelformat = desc.pixelformat;
+ v4l_format = desc.pixelformat;
+ }
+ }
+ if (fmt.fmt.pix.pixelformat == 0) {
+ ERROR_LOG(HLE, "V4L2: No supported format found");
+ return -1;
+ }
+
+ // select a frame size
+ fmt.fmt.pix.width = 0;
+ fmt.fmt.pix.height = 0;
+ struct v4l2_frmsizeenum frmsize;
+ memset(&frmsize, 0, sizeof(frmsize));
+ frmsize.pixel_format = fmt.fmt.pix.pixelformat;
+ while (ioctl(v4l_fd, VIDIOC_ENUM_FRAMESIZES, &frmsize) == 0) {
+ frmsize.index++;
+ if (frmsize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
+ INFO_LOG(HLE, "V4L2: frame size supported: %dx%d", frmsize.discrete.width, frmsize.discrete.height);
+ if (fmt.fmt.pix.width != 0 && fmt.fmt.pix.height != 0) {
+ continue;
+ } else if (frmsize.discrete.width >= ideal_width && frmsize.discrete.height >= ideal_height) {
+ fmt.fmt.pix.width = frmsize.discrete.width;
+ fmt.fmt.pix.height = frmsize.discrete.height;
+ }
+ }
+ }
+
+ if (fmt.fmt.pix.width == 0 && fmt.fmt.pix.height == 0) {
+ fmt.fmt.pix.width = ideal_width;
+ fmt.fmt.pix.height = ideal_height;
+ }
+ INFO_LOG(HLE, "V4L2: asking for %dx%d", fmt.fmt.pix.width, fmt.fmt.pix.height);
+ if (ioctl(v4l_fd, VIDIOC_S_FMT, &fmt) == -1) {
+ ERROR_LOG(HLE, "VIDIOC_S_FMT");
+ return -1;
+ }
+ v4l_hw_width = fmt.fmt.pix.width;
+ v4l_hw_height = fmt.fmt.pix.height;
+ INFO_LOG(HLE, "V4L2: will receive %dx%d", v4l_hw_width, v4l_hw_height);
+ v4l_height_fixed_aspect = v4l_hw_width * ideal_height / ideal_width;
+ INFO_LOG(HLE, "V4L2: will use %dx%d", v4l_hw_width, v4l_height_fixed_aspect);
+
+ // create a converter context
+ if (v4l_format == V4L2_PIX_FMT_YUYV) {
+ sws_context = sws_getContext(
+ v4l_hw_width, v4l_height_fixed_aspect, AV_PIX_FMT_YUYV422,
+ ideal_width, ideal_height, AV_PIX_FMT_RGB24,
+ SWS_BICUBIC, NULL, NULL, NULL);
+ } else if (v4l_format == V4L2_PIX_FMT_MJPEG) {
+ sws_context = sws_getContext(
+ v4l_hw_width, v4l_height_fixed_aspect, AV_PIX_FMT_RGB24,
+ ideal_width, ideal_height, AV_PIX_FMT_RGB24,
+ SWS_BICUBIC, NULL, NULL, NULL);
+ }
+
+
+ struct v4l2_requestbuffers req;
+ memset(&req, 0, sizeof(req));
+ req.count = 1;
+ req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ req.memory = V4L2_MEMORY_MMAP;
+ if (ioctl(v4l_fd, VIDIOC_REQBUFS, &req) == -1) {
+ ERROR_LOG(HLE, "VIDIOC_REQBUFS");
+ return -1;
+ }
+
+ struct v4l2_buffer buf;
+ memset(&buf, 0, sizeof(buf));
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = 0;
+ if (ioctl(v4l_fd, VIDIOC_QUERYBUF, &buf) == -1) {
+ ERROR_LOG(HLE, "VIDIOC_QUERYBUF");
+ return -1;
+ }
+
+ v4l_length = buf.length;
+ v4l_buffer = mmap(NULL,
+ v4l_length,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ v4l_fd, buf.m.offset);
+ if (v4l_buffer == MAP_FAILED) {
+ ERROR_LOG(HLE, "MAP_FAILED");
+ return -1;
+ }
+
+ memset(&buf, 0, sizeof(buf));
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ buf.type = type;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = 0;
+ if (ioctl(v4l_fd, VIDIOC_QBUF, &buf) == -1) {
+ ERROR_LOG(HLE, "VIDIOC_QBUF");
+ return -1;
+ }
+
+ if (ioctl(v4l_fd, VIDIOC_STREAMON, &type) == -1) {
+ ERROR_LOG(HLE, "VIDIOC_STREAMON");
+ return -1;
+ }
+
+ pthread_create(&v4l_thread, NULL, v4l_loop, NULL);
+
+ return 0;
+}
+
+int __v4l_stopCapture() {
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (v4l_fd < 0) {
+ goto exit;
+ }
+
+ if (ioctl(v4l_fd, VIDIOC_STREAMOFF, &type) == -1) {
+ ERROR_LOG(HLE, "VIDIOC_STREAMOFF");
+ goto exit;
+ }
+
+ if (munmap(v4l_buffer, v4l_length) == -1) {
+ ERROR_LOG(HLE, "munmap");
+ goto exit;
+ }
+
+ if (close(v4l_fd) == -1) {
+ ERROR_LOG(HLE, "close");
+ goto exit;
+ }
+
+ v4l_fd = -1;
+ //pthread_join(v4l_thread, NULL);
+
+exit:
+ v4l_fd = -1;
+ return 0;
+}
+
+#endif // PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID)
diff --git a/Core/HW/Camera.h b/Core/HW/Camera.h
new file mode 100644
index 0000000000..fb082c10cd
--- /dev/null
+++ b/Core/HW/Camera.h
@@ -0,0 +1,66 @@
+// 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 "ppsspp_config.h"
+#include "Core/Config.h"
+#include "Log.h"
+
+#if PPSSPP_PLATFORM(LINUX) && !PPSSPP_PLATFORM(ANDROID)
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "thread/threadutil.h"
+#include "Core/HLE/sceUsbCam.h"
+
+#include
+#include "ext/jpge/jpgd.h"
+#include "ext/jpge/jpge.h"
+
+extern "C" {
+#include "libswscale/swscale.h"
+#include "libavutil/imgutils.h"
+}
+
+ static int v4l_fd = -1;
+ static uint32_t v4l_format;
+ static int v4l_hw_width;
+ static int v4l_hw_height;
+ static int v4l_height_fixed_aspect;
+ static int v4l_ideal_width;
+ static int v4l_ideal_height;
+
+ static pthread_t v4l_thread;
+ static void *v4l_buffer;
+ static int v4l_length;
+ static struct SwsContext *sws_context;
+
+ std::vector __v4l_getDeviceList();
+ int __v4l_startCapture(int width, int height);
+ int __v4l_stopCapture();
+#endif
diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp
index 71c468467e..90130177a2 100644
--- a/UI/GameSettingsScreen.cpp
+++ b/UI/GameSettingsScreen.cpp
@@ -58,6 +58,7 @@
#include "Core/Reporting.h"
#include "Core/TextureReplacer.h"
#include "Core/WebServer.h"
+#include "Core/HLE/sceUsbCam.h"
#include "GPU/Common/PostShader.h"
#include "android/jni/TestRunner.h"
#include "GPU/GPUInterface.h"
@@ -68,7 +69,6 @@
#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)
@@ -263,14 +263,14 @@ void GameSettingsScreen::CreateViews() {
softwareGPU->SetEnabled(false);
}
-#if defined(_WIN32) && !PPSSPP_PLATFORM(UWP)
- if (winCamera && winCamera->getDeviceList().size() >= 1) {
+#if (defined(_WIN32) && !PPSSPP_PLATFORM(UWP)) || PPSSPP_PLATFORM(LINUX)
+ std::vector cameraList = Camera::getDeviceList();
+ if (cameraList.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()));
+ PopupMultiChoiceDynamic *cameraChoice = graphicsSettings->Add(new PopupMultiChoiceDynamic(&g_Config.sCameraDevice, gr->T("Camera Device"), cameraList, 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/Windows/CaptureDevice.cpp b/Windows/CaptureDevice.cpp
index ef0907f19e..429f4270ee 100644
--- a/Windows/CaptureDevice.cpp
+++ b/Windows/CaptureDevice.cpp
@@ -283,7 +283,7 @@ void ReaderCallback::imgConvert(
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)
@@ -453,7 +453,7 @@ bool WindowsCaptureDevice::start() {
switch (state) {
case CAPTUREDEVIDE_STATE::STOPPED:
for (auto &name : deviceList) {
- if (name == g_Config.sWinCameraDevice) {
+ if (name == g_Config.sCameraDevice) {
selection = count;
break;
}
@@ -510,7 +510,7 @@ bool WindowsCaptureDevice::start() {
);
if (SUCCEEDED(hr))
hr = setDeviceParam(pType);*/ // Don't support on Win7
-
+
// Request the first frame, in asnyc mode, OnReadSample will be called when ReadSample completed.
if (SUCCEEDED(hr)) {
hr = m_pReader->ReadSample(
diff --git a/android/src/org/ppsspp/ppsspp/NativeActivity.java b/android/src/org/ppsspp/ppsspp/NativeActivity.java
index 5676afee23..251a0f5ddc 100644
--- a/android/src/org/ppsspp/ppsspp/NativeActivity.java
+++ b/android/src/org/ppsspp/ppsspp/NativeActivity.java
@@ -1409,12 +1409,6 @@ public abstract class NativeActivity extends Activity implements SurfaceHolder.C
// Only keep the screen bright ingame.
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
- } else if (command.equals("event")) {
- if (params.equals("exitgame")) {
- if (mCameraHelper != null) {
- mCameraHelper.stopCamera();
- }
- }
}
return false;
}