Merge pull request #12580 from Florin9doi/v4l2b

Add camera support for linux (V4L2)
This commit is contained in:
Henrik Rydgård
2020-01-16 23:42:23 +01:00
committed by GitHub
13 changed files with 648 additions and 85 deletions
+2
View File
@@ -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
+2 -2
View File
@@ -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),
+1 -1
View File
@@ -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
+2
View File
@@ -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" />
@@ -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" />
+6
View File
@@ -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">
@@ -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" />
+12 -2
View File
@@ -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"},
@@ -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", '?', "" },
};
+156 -60
View File
@@ -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 __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<std::mutex> 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<std::mutex> 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>, "sceUsbCamSetupMic", 'i', "xxi" },
@@ -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" },
@@ -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", '?', "" },
@@ -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;
+79 -6
View File
@@ -17,16 +17,18 @@
#pragma once
#include <stdint.h>
#include <string>
#include <vector>
#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<std::string> 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,
+314
View File
@@ -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<std::string> __v4l_getDeviceList() {
std::vector<std::string> 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)
+66
View File
@@ -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 <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <malloc.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <vector>
#include "thread/threadutil.h"
#include "Core/HLE/sceUsbCam.h"
#include <linux/videodev2.h>
#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<std::string> __v4l_getDeviceList();
int __v4l_startCapture(int width, int height);
int __v4l_stopCapture();
#endif
+5 -5
View File
@@ -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 <shlobj.h>
#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<std::string> 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()));
+3 -3
View File
@@ -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(
@@ -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;
}