mirror of
https://github.com/RPCS3/rpcs3.git
synced 2024-11-25 12:12:50 +01:00
cellCamera: Add qt camera handler
This commit is contained in:
parent
ee7ed1fdc3
commit
08011e9b78
8
3rdparty/qt5.cmake
vendored
8
3rdparty/qt5.cmake
vendored
@ -2,17 +2,17 @@ add_library(3rdparty_qt5 INTERFACE)
|
||||
|
||||
set(QT_MIN_VER 5.15.2)
|
||||
|
||||
find_package(Qt5 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent)
|
||||
find_package(Qt5 ${QT_MIN_VER} CONFIG COMPONENTS Widgets Concurrent Multimedia)
|
||||
if(WIN32)
|
||||
find_package(Qt5 ${QT_MIN_VER} COMPONENTS WinExtras REQUIRED)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Concurrent)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::WinExtras Qt5::Concurrent Qt5::Multimedia)
|
||||
else()
|
||||
find_package(Qt5 ${QT_MIN_VER} COMPONENTS DBus Gui)
|
||||
if(Qt5DBus_FOUND)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Concurrent)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::DBus Qt5::Concurrent Qt5::Multimedia)
|
||||
target_compile_definitions(3rdparty_qt5 INTERFACE -DHAVE_QTDBUS)
|
||||
else()
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Concurrent)
|
||||
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Concurrent Qt5::Multimedia)
|
||||
endif()
|
||||
target_include_directories(3rdparty_qt5 INTERFACE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
@ -115,7 +115,8 @@ static bool check_dev_num(s32 dev_num)
|
||||
return dev_num == 0;
|
||||
}
|
||||
|
||||
static error_code check_camera_info(const CellCameraInfoEx& info)
|
||||
template <typename VariantOfCellCameraInfo>
|
||||
static error_code check_camera_info(const VariantOfCellCameraInfo& info)
|
||||
{
|
||||
// TODO: I managed to get 0x80990004 once. :thonkang:
|
||||
|
||||
@ -263,14 +264,88 @@ u32 get_video_buffer_size(const CellCameraInfoEx& info)
|
||||
u32 width, height;
|
||||
std::tie(width, height) = get_video_resolution(info);
|
||||
|
||||
const auto bpp = 4;
|
||||
return width * height * bpp;
|
||||
u32 bytes_per_pixel;
|
||||
|
||||
switch (info.format)
|
||||
{
|
||||
case CELL_CAMERA_RAW8:
|
||||
bytes_per_pixel = 1;
|
||||
break;
|
||||
case CELL_CAMERA_YUV422:
|
||||
case CELL_CAMERA_YUV420:
|
||||
case CELL_CAMERA_V_Y1_U_Y0:
|
||||
case CELL_CAMERA_RAW10:
|
||||
bytes_per_pixel = 2;
|
||||
break;
|
||||
case CELL_CAMERA_JPG:
|
||||
case CELL_CAMERA_RGBA:
|
||||
case CELL_CAMERA_FORMAT_UNKNOWN:
|
||||
default:
|
||||
bytes_per_pixel = 4;
|
||||
break;
|
||||
}
|
||||
|
||||
return width * height * bytes_per_pixel;
|
||||
}
|
||||
|
||||
// ************************
|
||||
// * cellCamera functions *
|
||||
// ************************
|
||||
|
||||
// This represents 4 almost identical subfunctions used by the Start/Stop/Reset/Close functions
|
||||
error_code check_init_and_open(s32 dev_num)
|
||||
{
|
||||
if (!check_dev_num(dev_num))
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
// TODO: Yet another CELL_CAMERA_ERROR_BUSY
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
if (!g_camera.init)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
if (!g_camera.is_open)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_OPEN;
|
||||
}
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// This represents a recurring subfunction throughout libCamera
|
||||
error_code check_resolution(s32 dev_num)
|
||||
{
|
||||
// TODO: Some sort of connection check maybe?
|
||||
error_code error = CELL_OK;
|
||||
|
||||
if (error == CELL_CAMERA_ERROR_RESOLUTION_UNKNOWN)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_TIMEOUT;
|
||||
}
|
||||
// TODO: Yet another CELL_CAMERA_ERROR_FATAL
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
// This represents a oftenly used sequence in libCamera (usually the beginning of a subfunction).
|
||||
// There also exist common sequences for mutex lock/unlock by the way.
|
||||
error_code check_resolution_ex(s32 dev_num)
|
||||
{
|
||||
// TODO: Yet another CELL_CAMERA_ERROR_BUSY
|
||||
|
||||
if (!check_dev_num(dev_num))
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
if (error_code error = check_resolution(dev_num))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
|
||||
error_code cellCameraInit()
|
||||
{
|
||||
cellCamera.todo("cellCameraInit()");
|
||||
@ -337,11 +412,7 @@ error_code cellCameraInit()
|
||||
|
||||
// TODO: Some other default attributes? Need to check the actual behaviour on a real PS3.
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::fake)
|
||||
{
|
||||
g_camera.is_attached = true;
|
||||
}
|
||||
|
||||
g_camera.is_attached = true;
|
||||
g_camera.init = 1;
|
||||
return CELL_OK;
|
||||
}
|
||||
@ -359,11 +430,7 @@ error_code cellCameraEnd()
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
// TODO: My tests hinted to this behavior, but I'm not sure, so I'll leave this commented
|
||||
//if (auto res = cellCameraClose(0))
|
||||
//{
|
||||
// return res;
|
||||
//}
|
||||
// TODO: call cellCameraClose(0), ignore errors
|
||||
|
||||
// TODO
|
||||
g_camera.init = 0;
|
||||
@ -371,9 +438,42 @@ error_code cellCameraEnd()
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellCameraOpen() // seems unused
|
||||
error_code cellCameraOpen(s32 dev_num, vm::ptr<CellCameraInfo> info)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
cellCamera.todo("cellCameraOpen(dev_num=%d, info=*0x%x)", dev_num, info);
|
||||
|
||||
if (!info)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
if (!g_camera.init)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
if (!check_dev_num(dev_num))
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (g_camera.is_open)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_ALREADY_OPEN;
|
||||
}
|
||||
|
||||
if (auto res = check_camera_info(*info))
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
if (!g_camera.is_attached)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@ -385,7 +485,7 @@ error_code cellCameraOpenAsync()
|
||||
|
||||
error_code cellCameraOpenEx(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
|
||||
{
|
||||
cellCamera.todo("cellCameraOpenEx(dev_num=%d, type=*0x%x)", dev_num, info);
|
||||
cellCamera.todo("cellCameraOpenEx(dev_num=%d, info=*0x%x)", dev_num, info);
|
||||
|
||||
// This function has a very weird order of checking for errors
|
||||
|
||||
@ -445,9 +545,29 @@ error_code cellCameraOpenEx(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
|
||||
|
||||
std::tie(info->width, info->height) = get_video_resolution(*info);
|
||||
|
||||
g_camera.handler.reset();
|
||||
g_camera.handler = Emu.GetCallbacks().get_camera_handler();
|
||||
|
||||
atomic_t<bool> wake_up = false;
|
||||
|
||||
Emu.CallAfter([&wake_up, handler = g_camera.handler]()
|
||||
{
|
||||
handler->open_camera();
|
||||
wake_up = true;
|
||||
wake_up.notify_one();
|
||||
});
|
||||
|
||||
while (!wake_up && !Emu.IsStopped())
|
||||
{
|
||||
thread_ctrl::wait_on(wake_up, false);
|
||||
}
|
||||
|
||||
g_camera.is_open = true;
|
||||
g_camera.info = *info;
|
||||
|
||||
cellCamera.notice("cellCameraOpen info: format=%d, resolution=%d, framerate=%d, bytesize=%d, width=%d, height=%d, dev_num=%d, guid=%d",
|
||||
info->format, info->resolution, info->framerate, info->bytesize, info->width, info->height, info->dev_num, info->guid);
|
||||
|
||||
auto& shared_data = g_fxo->get<gem_camera_shared>();
|
||||
shared_data.width = info->width > 0 ? +info->width : 640;
|
||||
shared_data.height = info->height > 0 ? +info->height : 480;
|
||||
@ -465,31 +585,45 @@ error_code cellCameraClose(s32 dev_num)
|
||||
{
|
||||
cellCamera.todo("cellCameraClose(dev_num=%d)", dev_num);
|
||||
|
||||
if (!check_dev_num(dev_num))
|
||||
if (error_code error = check_init_and_open(dev_num))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
// TODO: Yet another CELL_CAMERA_ERROR_BUSY
|
||||
|
||||
if (dev_num != 0)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (error_code error = check_resolution(dev_num))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
if (!g_camera.init)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return not_an_error(CELL_CAMERA_ERROR_NOT_OPEN);
|
||||
}
|
||||
|
||||
std::lock_guard lock(g_camera.mutex);
|
||||
|
||||
if (!g_camera.is_open)
|
||||
vm::dealloc(g_camera.info.buffer.addr(), vm::main);
|
||||
|
||||
if (g_camera.handler)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_OPEN;
|
||||
atomic_t<bool> wake_up = false;
|
||||
|
||||
Emu.CallAfter([&wake_up, handler = g_camera.handler]()
|
||||
{
|
||||
handler->close_camera();
|
||||
wake_up = true;
|
||||
wake_up.notify_one();
|
||||
});
|
||||
|
||||
while (!wake_up && !Emu.IsStopped())
|
||||
{
|
||||
thread_ctrl::wait_on(wake_up, false);
|
||||
}
|
||||
}
|
||||
|
||||
vm::dealloc(g_camera.info.buffer.addr(), vm::main);
|
||||
g_camera.is_open = false;
|
||||
|
||||
return CELL_OK;
|
||||
@ -509,7 +643,7 @@ error_code cellCameraClosePost()
|
||||
|
||||
error_code cellCameraGetDeviceGUID(s32 dev_num, vm::ptr<u32> guid)
|
||||
{
|
||||
cellCamera.todo("cellCameraGetDeviceGUID(dev_num=%d, guid=*0x%x)", dev_num, guid);
|
||||
cellCamera.notice("cellCameraGetDeviceGUID(dev_num=%d, guid=*0x%x)", dev_num, guid);
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
@ -518,9 +652,10 @@ error_code cellCameraGetDeviceGUID(s32 dev_num, vm::ptr<u32> guid)
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
// Does not check params or is_open (maybe attached?)
|
||||
|
||||
*guid = 0; // apparently always 0
|
||||
if (guid)
|
||||
{
|
||||
*guid = 0; // apparently always 0
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
@ -541,11 +676,16 @@ error_code cellCameraGetType(s32 dev_num, vm::ptr<s32> type)
|
||||
return not_an_error(CELL_CAMERA_ERROR_DEVICE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!check_dev_num(dev_num) || !type )
|
||||
if (!check_dev_num(dev_num) || !type)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (error_code error = check_resolution(dev_num))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!g_camera.is_attached)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
|
||||
@ -571,16 +711,16 @@ s32 cellCameraIsAvailable(s32 dev_num)
|
||||
return false;
|
||||
}
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
vm::var<s32> type;
|
||||
|
||||
if (!g_camera.init)
|
||||
if (cellCameraGetType(dev_num, type) != CELL_OK || *type == CELL_CAMERA_TYPE_UNKNOWN)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!check_dev_num(dev_num))
|
||||
if (*type > CELL_CAMERA_TYPE_UNKNOWN || *type <= CELL_CAMERA_USBVIDEOCLASS)
|
||||
{
|
||||
return false;
|
||||
// TODO: checks CELL_CAMERA_DEVICESPEED attribute
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -607,11 +747,18 @@ s32 cellCameraIsAttached(s32 dev_num)
|
||||
return false;
|
||||
}
|
||||
|
||||
vm::var<s32> type;
|
||||
|
||||
if (cellCameraGetType(dev_num, type) != CELL_OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard lock(g_camera.mutex);
|
||||
|
||||
bool is_attached = g_camera.is_attached;
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::fake)
|
||||
if (g_cfg.io.camera != camera_handler::null)
|
||||
{
|
||||
// "attach" camera here
|
||||
// normally should be attached immediately after event queue is registered, but just to be sure
|
||||
@ -627,7 +774,7 @@ s32 cellCameraIsAttached(s32 dev_num)
|
||||
|
||||
s32 cellCameraIsOpen(s32 dev_num)
|
||||
{
|
||||
cellCamera.warning("cellCameraIsOpen(dev_num=%d)", dev_num);
|
||||
cellCamera.notice("cellCameraIsOpen(dev_num=%d)", dev_num);
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
@ -653,7 +800,7 @@ s32 cellCameraIsOpen(s32 dev_num)
|
||||
|
||||
s32 cellCameraIsStarted(s32 dev_num)
|
||||
{
|
||||
cellCamera.warning("cellCameraIsStarted(dev_num=%d)", dev_num);
|
||||
cellCamera.notice("cellCameraIsStarted(dev_num=%d)", dev_num);
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
@ -689,22 +836,32 @@ error_code cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr<u32> arg1, vm
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return not_an_error(CELL_CAMERA_ERROR_DEVICE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!check_dev_num(dev_num) || !attr_name || !arg1) // invalid attributes don't have a name and at least arg1 should not be NULL
|
||||
if (!check_dev_num(dev_num))
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return not_an_error(CELL_CAMERA_ERROR_NOT_OPEN);
|
||||
}
|
||||
|
||||
// actually compares <= 0x63 which is equivalent
|
||||
if (attrib < CELL_CAMERA_FORMATCAP && !g_camera.is_open)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_OPEN;
|
||||
}
|
||||
|
||||
if (!arg1)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (error_code error = check_resolution(dev_num))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
std::lock_guard lock(g_camera.mutex);
|
||||
|
||||
if (!g_camera.is_attached)
|
||||
@ -712,15 +869,22 @@ error_code cellCameraGetAttribute(s32 dev_num, s32 attrib, vm::ptr<u32> arg1, vm
|
||||
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (!attr_name) // invalid attributes don't have a name
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (arg1)
|
||||
{
|
||||
*arg1 = g_camera.attr[attrib].v1;
|
||||
}
|
||||
|
||||
if (arg2)
|
||||
{
|
||||
*arg2 = g_camera.attr[attrib].v2;
|
||||
}
|
||||
|
||||
cellCamera.todo("cellCameraGetAttribute(attr_name=%s, v1=%d, v2=%d)", attr_name, g_camera.attr[attrib].v1, g_camera.attr[attrib].v2);
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@ -736,22 +900,32 @@ error_code cellCameraSetAttribute(s32 dev_num, s32 attrib, u32 arg1, u32 arg2)
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
if (!check_dev_num(dev_num))
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return not_an_error(CELL_CAMERA_ERROR_NOT_OPEN);
|
||||
}
|
||||
|
||||
if (!check_dev_num(dev_num) || !attr_name) // invalid attributes don't have a name
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
// actually compares <= 0x63 which is equivalent
|
||||
if (attrib < CELL_CAMERA_FORMATCAP && !g_camera.is_open)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_OPEN;
|
||||
}
|
||||
|
||||
if (error_code error = check_resolution(dev_num))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
if (!attr_name) // invalid attributes don't have a name
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
g_camera.set_attr(attrib, arg1, arg2);
|
||||
|
||||
return CELL_OK;
|
||||
@ -779,8 +953,6 @@ error_code cellCameraGetBufferSize(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
|
||||
return not_an_error(CELL_CAMERA_ERROR_DEVICE_NOT_FOUND);
|
||||
}
|
||||
|
||||
// the next few checks have a strange order, if I can trust the tests
|
||||
|
||||
if (!check_dev_num(dev_num))
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
@ -811,29 +983,24 @@ error_code cellCameraGetBufferSize(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
|
||||
return status;
|
||||
}
|
||||
|
||||
if (error_code error = check_resolution(dev_num))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
std::lock_guard lock(g_camera.mutex);
|
||||
|
||||
info->bytesize = get_video_buffer_size(g_camera.info);
|
||||
g_camera.info = *info;
|
||||
info->bytesize = get_video_buffer_size(g_camera.info);
|
||||
|
||||
return info->bytesize;
|
||||
cellCamera.notice("cellCameraGetBufferSize info: format=%d, resolution=%d, framerate=%d, bytesize=%d, width=%d, height=%d, dev_num=%d, guid=%d",
|
||||
info->format, info->resolution, info->framerate, info->bytesize, info->width, info->height, info->dev_num, info->guid);
|
||||
|
||||
return not_an_error(info->bytesize);
|
||||
}
|
||||
|
||||
error_code cellCameraGetBufferInfo()
|
||||
error_code check_get_camera_info(s32 dev_num, bool is_valid_info_struct)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
|
||||
// called by cellCameraGetBufferInfoEx
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
|
||||
{
|
||||
cellCamera.todo("cellCameraGetBufferInfoEx(dev_num=%d, read=0x%x)", dev_num, info);
|
||||
|
||||
// the following should be moved to cellCameraGetBufferInfo
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
if (!g_camera.init)
|
||||
@ -841,27 +1008,70 @@ error_code cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr<CellCameraInfoEx> info
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return not_an_error(CELL_CAMERA_ERROR_NOT_OPEN);
|
||||
}
|
||||
|
||||
if (!check_dev_num(dev_num))
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return not_an_error(CELL_CAMERA_ERROR_NOT_OPEN);
|
||||
}
|
||||
|
||||
if (!g_camera.is_open)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_OPEN;
|
||||
}
|
||||
|
||||
if (!info)
|
||||
if (!is_valid_info_struct)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellCameraGetBufferInfo(s32 dev_num, vm::ptr<CellCameraInfo> info)
|
||||
{
|
||||
cellCamera.todo("cellCameraGetBufferInfo(dev_num=%d, info=0x%x)", dev_num, info);
|
||||
|
||||
// called by cellCameraGetBufferInfoEx
|
||||
|
||||
if (error_code error = check_get_camera_info(dev_num, !!info))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
std::lock_guard lock(g_camera.mutex);
|
||||
|
||||
info->format = g_camera.info.format;
|
||||
info->resolution = g_camera.info.resolution;
|
||||
info->framerate = g_camera.info.framerate;
|
||||
info->buffer = g_camera.info.buffer;
|
||||
info->bytesize = g_camera.info.bytesize;
|
||||
info->width = g_camera.info.width;
|
||||
info->height = g_camera.info.height;
|
||||
info->dev_num = g_camera.info.dev_num;
|
||||
info->guid = g_camera.info.guid;
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
|
||||
{
|
||||
cellCamera.todo("cellCameraGetBufferInfoEx(dev_num=%d, info=0x%x)", dev_num, info);
|
||||
|
||||
// calls cellCameraGetBufferInfo
|
||||
|
||||
if (error_code error = check_get_camera_info(dev_num, !!info))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
std::lock_guard lock(g_camera.mutex);
|
||||
|
||||
*info = g_camera.info;
|
||||
|
||||
return CELL_OK;
|
||||
@ -869,13 +1079,56 @@ error_code cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr<CellCameraInfoEx> info
|
||||
|
||||
error_code cellCameraPrepExtensionUnit(s32 dev_num, vm::ptr<u8> guidExtensionCode)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
cellCamera.todo("cellCameraPrepExtensionUnit(dev_num=%d, guidExtensionCode=0x%x)", dev_num, guidExtensionCode);
|
||||
|
||||
if (!check_dev_num(dev_num) || !guidExtensionCode)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (error_code error = check_resolution(dev_num))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
if (!g_camera.is_attached)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
error_code cellCameraCtrlExtensionUnit(s32 dev_num, u8 request, u16 value, u16 length, vm::ptr<u8> data)
|
||||
{
|
||||
UNIMPLEMENTED_FUNC(cellCamera);
|
||||
cellCamera.todo("cellCameraCtrlExtensionUnit(dev_num=%d, request=%d, value=%d, length=%d, data=*0x%x)", dev_num, request, value, length, data);
|
||||
|
||||
if (!check_dev_num(dev_num))
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
if (!g_camera.is_open)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_OPEN;
|
||||
}
|
||||
|
||||
if (!data)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
}
|
||||
|
||||
if (!g_camera.is_attached)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// TODO: Yet another CELL_CAMERA_ERROR_PARAM
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@ -903,28 +1156,18 @@ error_code cellCameraReset(s32 dev_num)
|
||||
{
|
||||
cellCamera.todo("cellCameraReset(dev_num=%d)", dev_num);
|
||||
|
||||
if (!check_dev_num(dev_num))
|
||||
if (error_code error = check_init_and_open(dev_num))
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
return error;
|
||||
}
|
||||
|
||||
if (error_code error = check_resolution_ex(dev_num))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
if (!g_camera.init)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return not_an_error(CELL_CAMERA_ERROR_NOT_OPEN);
|
||||
}
|
||||
|
||||
if (!g_camera.is_open)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_OPEN;
|
||||
}
|
||||
|
||||
if (!g_camera.is_attached)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
|
||||
@ -951,35 +1194,45 @@ error_code cellCameraStart(s32 dev_num)
|
||||
{
|
||||
cellCamera.todo("cellCameraStart(dev_num=%d)", dev_num);
|
||||
|
||||
if (!check_dev_num(dev_num))
|
||||
if (error_code error = check_init_and_open(dev_num))
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
return error;
|
||||
}
|
||||
|
||||
if (error_code error = check_resolution_ex(dev_num))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
if (!g_camera.init)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return not_an_error(CELL_CAMERA_ERROR_NOT_OPEN);
|
||||
}
|
||||
|
||||
std::lock_guard lock(g_camera.mutex);
|
||||
|
||||
if (!g_camera.is_open)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_OPEN;
|
||||
}
|
||||
|
||||
if (!g_camera.is_attached)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
// TODO: Yet another CELL_CAMERA_ERROR_TIMEOUT
|
||||
|
||||
if (g_camera.handler)
|
||||
{
|
||||
g_camera.handler->set_mirrored(!!g_camera.attr[CELL_CAMERA_MIRRORFLAG].v1);
|
||||
g_camera.handler->set_frame_rate(g_camera.info.framerate);
|
||||
|
||||
atomic_t<bool> wake_up = false;
|
||||
|
||||
Emu.CallAfter([&wake_up, handler = g_camera.handler]()
|
||||
{
|
||||
handler->start_camera();
|
||||
wake_up = true;
|
||||
wake_up.notify_one();
|
||||
});
|
||||
|
||||
while (!wake_up && !Emu.IsStopped())
|
||||
{
|
||||
thread_ctrl::wait_on(wake_up, false);
|
||||
}
|
||||
}
|
||||
|
||||
g_camera.start_timestamp = get_guest_system_time();
|
||||
g_camera.is_streaming = true;
|
||||
|
||||
@ -1000,7 +1253,7 @@ error_code cellCameraStartPost()
|
||||
|
||||
error_code cellCameraRead(s32 dev_num, vm::ptr<u32> frame_num, vm::ptr<u32> bytes_read)
|
||||
{
|
||||
cellCamera.todo("cellCameraRead(dev_num=%d, frame_num=*0x%x, bytes_read=*0x%x)", dev_num, frame_num, bytes_read);
|
||||
cellCamera.notice("cellCameraRead(dev_num=%d, frame_num=*0x%x, bytes_read=*0x%x)", dev_num, frame_num, bytes_read);
|
||||
|
||||
vm::ptr<CellCameraReadEx> read_ex = vm::make_var<CellCameraReadEx>({});
|
||||
|
||||
@ -1030,7 +1283,7 @@ error_code cellCameraRead2()
|
||||
|
||||
error_code cellCameraReadEx(s32 dev_num, vm::ptr<CellCameraReadEx> read)
|
||||
{
|
||||
cellCamera.todo("cellCameraReadEx(dev_num=%d, read=0x%x)", dev_num, read);
|
||||
cellCamera.notice("cellCameraReadEx(dev_num=%d, read=0x%x)", dev_num, read);
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
@ -1079,6 +1332,46 @@ error_code cellCameraReadEx(s32 dev_num, vm::ptr<CellCameraReadEx> read)
|
||||
shared_data.frame_timestamp.exchange(read->timestamp);
|
||||
}
|
||||
|
||||
if (g_camera.handler)
|
||||
{
|
||||
u32 width{};
|
||||
u32 height{};
|
||||
u64 frame_number{};
|
||||
u64 bytes_read{};
|
||||
|
||||
atomic_t<bool> wake_up = false;
|
||||
bool result = false;
|
||||
|
||||
Emu.CallAfter([&]()
|
||||
{
|
||||
result = g_camera.handler->get_image(g_camera.info.buffer.get_ptr(), g_camera.info.bytesize, width, height, frame_number, bytes_read);
|
||||
wake_up = true;
|
||||
wake_up.notify_one();
|
||||
});
|
||||
|
||||
while (!wake_up && !Emu.IsStopped())
|
||||
{
|
||||
thread_ctrl::wait_on(wake_up, false);
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
g_camera.is_streaming = false;
|
||||
g_camera.is_attached = false;
|
||||
g_camera.is_open = false;
|
||||
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
|
||||
}
|
||||
|
||||
if (read)
|
||||
{
|
||||
read->frame = frame_number;
|
||||
read->bytesread = bytes_read;
|
||||
}
|
||||
|
||||
cellCamera.trace("cellCameraRead: frame_number=%d, width=%d, height=%d. bytes_read=%d (passed to game: frame=%d, bytesread=%d)",
|
||||
frame_number, width, height, bytes_read, read ? read->frame.get() : 0, read ? read->bytesread.get() : 0);
|
||||
}
|
||||
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
@ -1093,28 +1386,18 @@ error_code cellCameraStop(s32 dev_num)
|
||||
{
|
||||
cellCamera.todo("cellCameraStop(dev_num=%d)", dev_num);
|
||||
|
||||
if (!check_dev_num(dev_num))
|
||||
if (error_code error = check_init_and_open(dev_num))
|
||||
{
|
||||
return CELL_CAMERA_ERROR_PARAM;
|
||||
return error;
|
||||
}
|
||||
|
||||
if (error_code error = check_resolution_ex(dev_num))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
if (!g_camera.init)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return not_an_error(CELL_CAMERA_ERROR_NOT_OPEN);
|
||||
}
|
||||
|
||||
if (!g_camera.is_open)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_NOT_OPEN;
|
||||
}
|
||||
|
||||
if (!g_camera.is_attached)
|
||||
{
|
||||
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
|
||||
@ -1125,6 +1408,23 @@ error_code cellCameraStop(s32 dev_num)
|
||||
return CELL_CAMERA_ERROR_NOT_STARTED;
|
||||
}
|
||||
|
||||
if (g_camera.handler)
|
||||
{
|
||||
atomic_t<bool> wake_up = false;
|
||||
|
||||
Emu.CallAfter([&wake_up, handler = g_camera.handler]()
|
||||
{
|
||||
handler->stop_camera();
|
||||
wake_up = true;
|
||||
wake_up.notify_one();
|
||||
});
|
||||
|
||||
while (!wake_up && !Emu.IsStopped())
|
||||
{
|
||||
thread_ctrl::wait_on(wake_up, false);
|
||||
}
|
||||
}
|
||||
|
||||
g_camera.is_streaming = false;
|
||||
|
||||
return CELL_OK;
|
||||
@ -1158,6 +1458,11 @@ error_code cellCameraSetNotifyEventQueue(u64 key)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
if (error_code error = check_resolution(0))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
g_camera.add_queue(key, 0, 0);
|
||||
|
||||
return CELL_OK;
|
||||
@ -1179,6 +1484,11 @@ error_code cellCameraRemoveNotifyEventQueue(u64 key)
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
if (error_code error = check_resolution(0))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
g_camera.remove_queue(key);
|
||||
|
||||
return CELL_OK;
|
||||
@ -1188,11 +1498,6 @@ error_code cellCameraSetNotifyEventQueue2(u64 key, u64 source, u64 flag)
|
||||
{
|
||||
cellCamera.todo("cellCameraSetNotifyEventQueue2(key=0x%x, source=%d, flag=%d)", key, source, flag);
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
auto& g_camera = g_fxo->get<camera_thread>();
|
||||
|
||||
if (!g_camera.init)
|
||||
@ -1200,6 +1505,16 @@ error_code cellCameraSetNotifyEventQueue2(u64 key, u64 source, u64 flag)
|
||||
return CELL_CAMERA_ERROR_NOT_INIT;
|
||||
}
|
||||
|
||||
if (g_cfg.io.camera == camera_handler::null)
|
||||
{
|
||||
return CELL_OK;
|
||||
}
|
||||
|
||||
if (error_code error = check_resolution(0))
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
g_camera.add_queue(key, source, flag);
|
||||
|
||||
return CELL_OK;
|
||||
@ -1341,6 +1656,7 @@ void camera_context::reset_state()
|
||||
|
||||
std::scoped_lock lock(mutex_notify_data_map);
|
||||
notify_data_map.clear();
|
||||
handler.reset();
|
||||
}
|
||||
|
||||
void camera_context::send_attach_state(bool attached)
|
||||
@ -1372,7 +1688,9 @@ void camera_context::send_attach_state(bool attached)
|
||||
|
||||
void camera_context::set_attr(s32 attrib, u32 arg1, u32 arg2)
|
||||
{
|
||||
if (attrib == CELL_CAMERA_READMODE)
|
||||
switch (attrib)
|
||||
{
|
||||
case CELL_CAMERA_READMODE:
|
||||
{
|
||||
if (arg1 != CELL_CAMERA_READ_FUNCCALL && arg1 != CELL_CAMERA_READ_DIRECT)
|
||||
{
|
||||
@ -1380,6 +1698,18 @@ void camera_context::set_attr(s32 attrib, u32 arg1, u32 arg2)
|
||||
arg1 = CELL_CAMERA_READ_FUNCCALL;
|
||||
}
|
||||
read_mode.exchange(arg1);
|
||||
break;
|
||||
}
|
||||
case CELL_CAMERA_MIRRORFLAG:
|
||||
{
|
||||
if (handler)
|
||||
{
|
||||
handler->set_mirrored(!!arg1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
std::lock_guard lock(mutex);
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "Utilities/Timer.h"
|
||||
#include "Emu/Cell/lv2/sys_memory.h"
|
||||
#include "Utilities/Thread.h"
|
||||
#include "Emu/Io/camera_handler_base.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
@ -436,6 +437,8 @@ public:
|
||||
atomic_t<u32> init = 0;
|
||||
|
||||
static constexpr auto thread_name = "Camera Thread"sv;
|
||||
|
||||
std::shared_ptr<camera_handler_base> handler;
|
||||
};
|
||||
|
||||
using camera_thread = named_thread<camera_context>;
|
||||
|
50
rpcs3/Emu/Io/Null/null_camera_handler.h
Normal file
50
rpcs3/Emu/Io/Null/null_camera_handler.h
Normal file
@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Io/camera_handler_base.h"
|
||||
|
||||
class null_camera_handler final : public camera_handler_base
|
||||
{
|
||||
public:
|
||||
null_camera_handler() : camera_handler_base() {}
|
||||
|
||||
void open_camera() override {};
|
||||
void close_camera() override {};
|
||||
void start_camera() override {};
|
||||
void stop_camera() override {};
|
||||
|
||||
void set_format(s32 format, u32 bytes_per_pixel) override
|
||||
{
|
||||
m_format = format;
|
||||
m_bytes_per_pixel = bytes_per_pixel;
|
||||
}
|
||||
|
||||
void set_frame_rate(u32 frame_rate) override
|
||||
{
|
||||
m_frame_rate = frame_rate;
|
||||
}
|
||||
|
||||
void set_resolution(u32 width, u32 height) override
|
||||
{
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
};
|
||||
|
||||
void set_mirrored(bool mirrored) override
|
||||
{
|
||||
m_mirrored = mirrored;
|
||||
}
|
||||
|
||||
u64 frame_number() const override
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool get_image(u8* /*buf*/, u64 /*size*/, u32& width, u32& height, u64& frame_number, u64& bytes_read) override
|
||||
{
|
||||
width = 0;
|
||||
height = 0;
|
||||
frame_number = 0;
|
||||
bytes_read = 0;
|
||||
return true;
|
||||
}
|
||||
};
|
42
rpcs3/Emu/Io/camera_handler_base.h
Normal file
42
rpcs3/Emu/Io/camera_handler_base.h
Normal file
@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
class camera_handler_base
|
||||
{
|
||||
public:
|
||||
enum class camera_handler_state
|
||||
{
|
||||
closed,
|
||||
open,
|
||||
running
|
||||
};
|
||||
|
||||
virtual ~camera_handler_base() = default;
|
||||
|
||||
virtual void open_camera() = 0;
|
||||
virtual void close_camera() = 0;
|
||||
virtual void start_camera() = 0;
|
||||
virtual void stop_camera() = 0;
|
||||
|
||||
virtual void set_format(s32 format, u32 bytes_per_pixel) = 0;
|
||||
virtual void set_frame_rate(u32 frame_rate) = 0;
|
||||
virtual void set_resolution(u32 width, u32 height) = 0;
|
||||
virtual void set_mirrored(bool mirrored) = 0;
|
||||
|
||||
virtual u64 frame_number() const = 0; // Convenience function to check if there's a new frame.
|
||||
virtual bool get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read) = 0;
|
||||
|
||||
camera_handler_state get_state() const { return m_state.load(); };
|
||||
|
||||
protected:
|
||||
std::mutex m_mutex;
|
||||
atomic_t<camera_handler_state> m_state = camera_handler_state::closed;
|
||||
bool m_mirrored = false;
|
||||
s32 m_format = 2; // CELL_CAMERA_RAW8
|
||||
u32 m_bytes_per_pixel = 1;
|
||||
u32 m_width = 640;
|
||||
u32 m_height = 480;
|
||||
u32 m_frame_rate = 30;
|
||||
};
|
@ -635,7 +635,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
|
||||
|
||||
if (!add_only)
|
||||
{
|
||||
if (m_config_mode == cfg_mode::custom_selection || m_config_mode == cfg_mode::continuous)
|
||||
if (m_config_mode == cfg_mode::custom_selection || (m_config_mode == cfg_mode::continuous && !m_config_path.empty()))
|
||||
{
|
||||
if (fs::file cfg_file{ m_config_path })
|
||||
{
|
||||
|
@ -70,6 +70,7 @@ struct EmuCallbacks
|
||||
std::function<void(std::string_view title_id)> init_pad_handler;
|
||||
std::function<std::unique_ptr<class GSFrameBase>()> get_gs_frame;
|
||||
std::function<void()> init_gs_render;
|
||||
std::function<std::shared_ptr<class camera_handler_base>()> get_camera_handler;
|
||||
std::function<std::shared_ptr<class AudioBackend>()> get_audio;
|
||||
std::function<std::shared_ptr<class MsgDialogBase>()> get_msg_dialog;
|
||||
std::function<std::shared_ptr<class OskDialogBase>()> get_osk_dialog;
|
||||
|
@ -243,6 +243,7 @@ struct cfg_root : cfg::node
|
||||
cfg::_enum<mouse_handler> mouse{ this, "Mouse", mouse_handler::basic };
|
||||
cfg::_enum<camera_handler> camera{ this, "Camera", camera_handler::null };
|
||||
cfg::_enum<fake_camera_type> camera_type{ this, "Camera type", fake_camera_type::unknown };
|
||||
cfg::_enum<camera_flip> camera_flip{ this, "Camera flip", camera_flip::none, true };
|
||||
cfg::_enum<move_handler> move{ this, "Move", move_handler::null };
|
||||
cfg::_enum<buzz_handler> buzz{ this, "Buzz emulated controller", buzz_handler::null };
|
||||
cfg::_enum<turntable_handler> turntable{this, "Turntable emulated controller", turntable_handler::null};
|
||||
|
@ -334,6 +334,7 @@ void fmt_class_string<camera_handler>::format(std::string& out, u64 arg)
|
||||
{
|
||||
case camera_handler::null: return "Null";
|
||||
case camera_handler::fake: return "Fake";
|
||||
case camera_handler::qt: return "Qt";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
@ -357,6 +358,23 @@ void fmt_class_string<fake_camera_type>::format(std::string& out, u64 arg)
|
||||
});
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<camera_flip>::format(std::string& out, u64 arg)
|
||||
{
|
||||
format_enum(out, arg, [](auto value)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case camera_flip::none: return "None";
|
||||
case camera_flip::horizontal: return "Horizontal";
|
||||
case camera_flip::vertical: return "Vertical";
|
||||
case camera_flip::both: return "Both";
|
||||
}
|
||||
|
||||
return unknown;
|
||||
});
|
||||
}
|
||||
|
||||
template <>
|
||||
void fmt_class_string<move_handler>::format(std::string& out, u64 arg)
|
||||
{
|
||||
|
@ -78,6 +78,15 @@ enum class camera_handler
|
||||
{
|
||||
null,
|
||||
fake,
|
||||
qt
|
||||
};
|
||||
|
||||
enum class camera_flip
|
||||
{
|
||||
none,
|
||||
horizontal,
|
||||
vertical,
|
||||
both
|
||||
};
|
||||
|
||||
enum class fake_camera_type
|
||||
|
@ -445,6 +445,8 @@
|
||||
<ClInclude Include="Emu\Cell\Modules\cellStorage.h" />
|
||||
<ClInclude Include="Emu\Cell\Modules\libfs_utility_init.h" />
|
||||
<ClInclude Include="Emu\Cell\Modules\sys_crashdump.h" />
|
||||
<ClInclude Include="Emu\Io\camera_handler_base.h" />
|
||||
<ClInclude Include="Emu\Io\Null\null_camera_handler.h" />
|
||||
<ClInclude Include="Emu\Io\Turntable.h" />
|
||||
<ClInclude Include="Emu\Io\GHLtar.h" />
|
||||
<ClInclude Include="Emu\Io\Buzz.h" />
|
||||
|
@ -1999,6 +1999,12 @@
|
||||
<ClInclude Include="Emu\RSX\Common\bitfield.hpp">
|
||||
<Filter>Emu\GPU\RSX\Common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Io\camera_handler_base.h">
|
||||
<Filter>Emu\Io</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Emu\Io\Null\null_camera_handler.h">
|
||||
<Filter>Emu\Io\Null</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl">
|
||||
|
@ -71,7 +71,7 @@
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\release;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\release;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtMultimedia;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>-Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AssemblerListingLocation>release\</AssemblerListingLocation>
|
||||
<BrowseInformation>false</BrowseInformation>
|
||||
@ -79,7 +79,7 @@
|
||||
<DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<ObjectFileName>$(IntDir)</ObjectFileName>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<PreprocessorDefinitions>_WINDOWS;UNICODE;WIN32;WIN64;WITH_DISCORD_RPC;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;NDEBUG;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_WINDOWS;UNICODE;WIN32;WIN64;WITH_DISCORD_RPC;QT_NO_DEBUG;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;NDEBUG;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;QT_MULTIMEDIA_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessToFile>false</PreprocessToFile>
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||
@ -88,7 +88,7 @@
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;OpenAL.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmain.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Core.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5WinExtras.lib;Qt5Concurrent.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;OpenAL.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslang.lib;OSDependent.lib;OGLCompiler.lib;SPIRV.lib;MachineIndependent.lib;GenericCodeGen.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmain.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgets.lib;$(QTDIR)\lib\Qt5Gui.lib;$(QTDIR)\lib\Qt5Core.lib;Qt5Core.lib;Qt5Gui.lib;Qt5Widgets.lib;Qt5WinExtras.lib;Qt5Concurrent.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimedia.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\3rdparty\OpenAL\libs\Win64;..\3rdparty\glslang\build\hlsl\Release;..\3rdparty\glslang\build\SPIRV\Release;..\3rdparty\glslang\build\OGLCompilersDLL\Release;..\3rdparty\glslang\build\glslang\OSDependent\Windows\Release;..\3rdparty\glslang\build\glslang\Release;..\3rdparty\SPIRV\build\source\Release;..\3rdparty\SPIRV\build\source\opt\Release;..\lib\$(CONFIGURATION)-$(PLATFORM);..\3rdparty\XAudio2Redist\libs;..\3rdparty\discord-rpc\lib;$(QTDIR)\lib;%(AdditionalLibraryDirectories);$(VULKAN_SDK)\Lib</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions>
|
||||
<DataExecutionPrevention>true</DataExecutionPrevention>
|
||||
@ -122,7 +122,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\debug;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalIncludeDirectories>..\3rdparty\flatbuffers\include;..\3rdparty\wolfssl\wolfssl;..\3rdparty\curl\curl\include;..\3rdparty\libusb\libusb\libusb;$(VULKAN_SDK)\Include;..\3rdparty\XAudio2Redist\include;$(QTDIR)\include;$(QTDIR)\include\QtWidgets;$(QTDIR)\include\QtGui;$(QTDIR)\include\QtANGLE;$(QTDIR)\include\QtCore;.\debug;$(QTDIR)\mkspecs\win32-msvc2015;.\QTGeneratedFiles\$(ConfigurationName);.\QTGeneratedFiles;$(QTDIR)\include\QtWinExtras;$(QTDIR)\include\QtConcurrent;$(QTDIR)\include\QtMultimedia;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<AdditionalOptions>-Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
|
||||
<AssemblerListingLocation>debug\</AssemblerListingLocation>
|
||||
<BrowseInformation>false</BrowseInformation>
|
||||
@ -130,7 +130,7 @@
|
||||
<DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<ObjectFileName>$(IntDir)</ObjectFileName>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_WINDOWS;UNICODE;WIN32;WIN64;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessorDefinitions>_WINDOWS;UNICODE;WIN32;WIN64;QT_WIDGETS_LIB;QT_GUI_LIB;QT_CORE_LIB;QT_WINEXTRAS_LIB;QT_CONCURRENT_LIB;QT_MULTIMEDIA_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<PreprocessToFile>false</PreprocessToFile>
|
||||
<RuntimeTypeInfo>true</RuntimeTypeInfo>
|
||||
<SuppressStartupBanner>true</SuppressStartupBanner>
|
||||
@ -139,7 +139,7 @@
|
||||
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;OpenAL.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmaind.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgetsd.lib;$(QTDIR)\lib\Qt5Guid.lib;$(QTDIR)\lib\Qt5Cored.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5WinExtrasd.lib;Qt5Concurrentd.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Ole32.lib;gdi32.lib;..\hidapi.lib;..\libusb-1.0.lib;winmm.lib;OpenAL.lib;XAudio.lib;GLGSRender.lib;shlwapi.lib;VKGSRender.lib;vulkan-1.lib;wolfssl.lib;libcurl.lib;Wldap32.lib;glslangd.lib;OSDependentd.lib;OGLCompilerd.lib;SPIRVd.lib;MachineIndependentd.lib;GenericCodeGend.lib;Advapi32.lib;user32.lib;zlib.lib;..\libpng16.lib;asmjit.lib;yaml-cpp.lib;discord-rpc.lib;emucore.lib;dxgi.lib;$(QTDIR)\lib\qtmaind.lib;shell32.lib;$(QTDIR)\lib\Qt5Widgetsd.lib;$(QTDIR)\lib\Qt5Guid.lib;$(QTDIR)\lib\Qt5Cored.lib;Qt5Cored.lib;Qt5Guid.lib;Qt5Widgetsd.lib;Qt5WinExtrasd.lib;Qt5Concurrentd.lib;7zlib.lib;SPIRV-Tools.lib;SPIRV-Tools-opt.lib;Qt5Multimediad.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>..\3rdparty\OpenAL\libs\Win64;..\3rdparty\glslang\build\hlsl\Debug;..\3rdparty\glslang\build\SPIRV\Debug;..\3rdparty\glslang\build\OGLCompilersDLL\Debug;..\3rdparty\glslang\build\glslang\OSDependent\Windows\Debug;..\3rdparty\glslang\build\glslang\Debug;..\3rdparty\SPIRV\build\source\opt\Debug;..\3rdparty\XAudio2Redist\libs;..\3rdparty\discord-rpc\lib;..\lib\$(CONFIGURATION)-$(PLATFORM);$(QTDIR)\lib;%(AdditionalLibraryDirectories);$(VULKAN_SDK)\Lib</AdditionalLibraryDirectories>
|
||||
<AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" /VERBOSE %(AdditionalOptions)</AdditionalOptions>
|
||||
<DataExecutionPrevention>true</DataExecutionPrevention>
|
||||
@ -594,6 +594,7 @@
|
||||
<ClCompile Include="rpcs3qt\patch_manager_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\pkg_install_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\persistent_settings.cpp" />
|
||||
<ClCompile Include="rpcs3qt\qt_camera_handler.cpp" />
|
||||
<ClCompile Include="rpcs3qt\recvmessage_dialog_frame.cpp" />
|
||||
<ClCompile Include="rpcs3qt\render_creator.cpp" />
|
||||
<ClCompile Include="rpcs3qt\rpcn_settings_dialog.cpp" />
|
||||
@ -604,6 +605,7 @@
|
||||
<ClCompile Include="rpcs3qt\skylander_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\tooltips.cpp" />
|
||||
<ClCompile Include="rpcs3qt\update_manager.cpp" />
|
||||
<ClCompile Include="rpcs3qt\qt_camera_video_surface.cpp" />
|
||||
<ClCompile Include="rpcs3qt\_discord_utils.cpp" />
|
||||
<ClCompile Include="rpcs3qt\find_dialog.cpp" />
|
||||
<ClCompile Include="rpcs3qt\game_compatibility.cpp" />
|
||||
@ -1088,6 +1090,7 @@
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\flatbuffers\include" "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="rpcs3qt\qt_camera_handler.h" />
|
||||
<ClInclude Include="rpcs3qt\richtext_item_delegate.h" />
|
||||
<CustomBuild Include="rpcs3qt\sendmessage_dialog_frame.h">
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
@ -1130,6 +1133,7 @@
|
||||
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
|
||||
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DWITH_DISCORD_RPC -DQT_NO_DEBUG -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DNDEBUG -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -D%(PreprocessorDefinitions) "-I.\..\3rdparty\wolfssl\wolfssl" "-I.\..\3rdparty\curl\curl\include" "-I.\..\3rdparty\libusb\libusb\libusb" "-I$(VULKAN_SDK)\Include" "-I.\..\3rdparty\XAudio2Redist\include" "-I$(QTDIR)\include" "-I$(QTDIR)\include\QtWidgets" "-I$(QTDIR)\include\QtGui" "-I$(QTDIR)\include\QtANGLE" "-I$(QTDIR)\include\QtCore" "-I.\release" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent"</Command>
|
||||
</CustomBuild>
|
||||
<ClInclude Include="rpcs3qt\qt_camera_video_surface.h" />
|
||||
<ClInclude Include="rpcs3qt\_discord_utils.h" />
|
||||
<CustomBuild Include="rpcs3qt\find_dialog.h">
|
||||
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
|
||||
|
@ -133,6 +133,9 @@
|
||||
<Filter Include="Gui\rpcn">
|
||||
<UniqueIdentifier>{dfd401ec-dd22-4f8a-9dea-565a03efab6a}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Io\camera">
|
||||
<UniqueIdentifier>{f1996891-48b7-441a-b3fe-52794cbebe80}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="main.cpp">
|
||||
@ -777,6 +780,12 @@
|
||||
<ClCompile Include="QTGeneratedFiles\Release\moc_recvmessage_dialog_frame.cpp">
|
||||
<Filter>Generated Files\Release</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\qt_camera_handler.cpp">
|
||||
<Filter>Io\camera</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="rpcs3qt\qt_camera_video_surface.cpp">
|
||||
<Filter>Io\camera</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Input\ds4_pad_handler.h">
|
||||
@ -911,6 +920,12 @@
|
||||
<ClInclude Include="QTGeneratedFiles\ui_patch_creator_dialog.h">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\qt_camera_video_surface.h">
|
||||
<Filter>Io\camera</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="rpcs3qt\qt_camera_handler.h">
|
||||
<Filter>Io\camera</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<CustomBuild Include="debug\moc_predefs.h.cbt">
|
||||
|
@ -49,6 +49,8 @@ set(SRC_FILES
|
||||
persistent_settings.cpp
|
||||
pkg_install_dialog.cpp
|
||||
progress_dialog.cpp
|
||||
qt_camera_handler.cpp
|
||||
qt_camera_video_surface.cpp
|
||||
qt_utils.cpp
|
||||
register_editor_dialog.cpp
|
||||
recvmessage_dialog_frame.cpp
|
||||
|
@ -1007,11 +1007,21 @@ QString emu_settings::GetLocalizedSetting(const QString& original, emu_settings_
|
||||
case fake_camera_type::uvc1_1: return tr("UVC 1.1", "Camera type");
|
||||
}
|
||||
break;
|
||||
case emu_settings_type::CameraFlip:
|
||||
switch (static_cast<camera_flip>(index))
|
||||
{
|
||||
case camera_flip::none: return tr("No", "Camera flip");
|
||||
case camera_flip::horizontal: return tr("Flip horizontally", "Camera flip");
|
||||
case camera_flip::vertical: return tr("Flip vertically", "Camera flip");
|
||||
case camera_flip::both: return tr("Flip both axis", "Camera flip");
|
||||
}
|
||||
break;
|
||||
case emu_settings_type::Camera:
|
||||
switch (static_cast<camera_handler>(index))
|
||||
{
|
||||
case camera_handler::null: return tr("Null", "Camera handler");
|
||||
case camera_handler::fake: return tr("Fake", "Camera handler");
|
||||
case camera_handler::qt: return tr("Qt", "Camera handler");
|
||||
}
|
||||
break;
|
||||
case emu_settings_type::Move:
|
||||
|
@ -125,6 +125,7 @@ enum class emu_settings_type
|
||||
MouseHandler,
|
||||
Camera,
|
||||
CameraType,
|
||||
CameraFlip,
|
||||
Move,
|
||||
Buzz,
|
||||
Turntable,
|
||||
@ -279,6 +280,7 @@ inline static const QMap<emu_settings_type, cfg_location> settings_location =
|
||||
{ emu_settings_type::MouseHandler, { "Input/Output", "Mouse"}},
|
||||
{ emu_settings_type::Camera, { "Input/Output", "Camera"}},
|
||||
{ emu_settings_type::CameraType, { "Input/Output", "Camera type"}},
|
||||
{ emu_settings_type::CameraFlip, { "Input/Output", "Camera flip"}},
|
||||
{ emu_settings_type::Move, { "Input/Output", "Move" }},
|
||||
{ emu_settings_type::Buzz, { "Input/Output", "Buzz emulated controller" }},
|
||||
{ emu_settings_type::Turntable, { "Input/Output", "Turntable emulated controller" }},
|
||||
|
@ -15,11 +15,13 @@
|
||||
#include "gl_gs_frame.h"
|
||||
#include "display_sleep_control.h"
|
||||
#include "localized_emu.h"
|
||||
#include "qt_camera_handler.h"
|
||||
|
||||
#ifdef WITH_DISCORD_RPC
|
||||
#include "_discord_utils.h"
|
||||
#endif
|
||||
|
||||
#include "Emu/Io/Null/null_camera_handler.h"
|
||||
#include "Emu/Cell/Modules/cellAudio.h"
|
||||
#include "Emu/RSX/Overlays/overlay_perf_metrics.h"
|
||||
#include "Emu/system_utils.hpp"
|
||||
@ -349,6 +351,21 @@ void gui_application::InitializeCallbacks()
|
||||
}
|
||||
};
|
||||
|
||||
callbacks.get_camera_handler = []() -> std::shared_ptr<camera_handler_base>
|
||||
{
|
||||
switch (g_cfg.io.camera.get())
|
||||
{
|
||||
case camera_handler::null:
|
||||
case camera_handler::fake:
|
||||
{
|
||||
return std::make_shared<null_camera_handler>();
|
||||
}
|
||||
case camera_handler::qt:
|
||||
{
|
||||
return std::make_shared<qt_camera_handler>();
|
||||
}
|
||||
}
|
||||
};
|
||||
callbacks.get_gs_frame = [this]() -> std::unique_ptr<GSFrameBase> { return get_gs_frame(); };
|
||||
callbacks.get_msg_dialog = [this]() -> std::shared_ptr<MsgDialogBase> { return m_show_gui ? std::make_shared<msg_dialog_frame>() : nullptr; };
|
||||
callbacks.get_osk_dialog = [this]() -> std::shared_ptr<OskDialogBase> { return m_show_gui ? std::make_shared<osk_dialog_frame>() : nullptr; };
|
||||
|
333
rpcs3/rpcs3qt/qt_camera_handler.cpp
Normal file
333
rpcs3/rpcs3qt/qt_camera_handler.cpp
Normal file
@ -0,0 +1,333 @@
|
||||
#include "stdafx.h"
|
||||
#include "qt_camera_handler.h"
|
||||
#include "Emu/System.h"
|
||||
|
||||
#include <QMediaService>
|
||||
#include <QCameraInfo>
|
||||
|
||||
LOG_CHANNEL(camera_log, "Camera");
|
||||
|
||||
qt_camera_handler::qt_camera_handler() : camera_handler_base()
|
||||
{
|
||||
}
|
||||
|
||||
qt_camera_handler::~qt_camera_handler()
|
||||
{
|
||||
close_camera();
|
||||
}
|
||||
|
||||
void qt_camera_handler::set_camera(const QCameraInfo& cameraInfo)
|
||||
{
|
||||
if (cameraInfo.isNull())
|
||||
{
|
||||
camera_log.error("No camera present");
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine if the camera is front facing, in which case we will need to flip the image horizontally.
|
||||
const bool front_facing = cameraInfo.position() == QCamera::Position::FrontFace;
|
||||
|
||||
camera_log.success("Using camera: name=\"%s\", description=\"%s\", front_facing=%d", cameraInfo.deviceName().toStdString(), cameraInfo.description().toStdString(), front_facing);
|
||||
|
||||
// Create camera and video surface
|
||||
m_surface.reset(new qt_camera_video_surface(front_facing, nullptr));
|
||||
m_camera.reset(new QCamera(cameraInfo));
|
||||
|
||||
// Create connects (may not work due to threading)
|
||||
connect(m_camera.get(), &QCamera::stateChanged, this, [this](QCamera::State state){ handle_camera_state(state); });
|
||||
connect(m_camera.get(), &QCamera::statusChanged, this, [this](QCamera::Status status){ handle_camera_status(status); });
|
||||
connect(m_camera.get(), &QCamera::errorOccurred, this, [this](QCamera::Error error){ handle_camera_error(error); });
|
||||
connect(m_camera.get(), &QCamera::captureModeChanged, this, [this](QCamera::CaptureModes modes){ handle_capture_modes(modes); });
|
||||
connect(m_camera.get(), QOverload<QCamera::LockStatus, QCamera::LockChangeReason>::of(&QCamera::lockStatusChanged), this, [this](QCamera::LockStatus status, QCamera::LockChangeReason reason){ handle_lock_status(status, reason); });
|
||||
|
||||
// Set view finder and update the settings
|
||||
m_camera->setViewfinder(m_surface.get());
|
||||
update_camera_settings();
|
||||
|
||||
// Log some states
|
||||
handle_camera_state(m_camera->state());
|
||||
handle_lock_status(m_camera->lockStatus(), QCamera::UserRequest);
|
||||
}
|
||||
|
||||
void qt_camera_handler::open_camera()
|
||||
{
|
||||
// List available cameras
|
||||
for (const QCameraInfo& cameraInfo : QCameraInfo::availableCameras())
|
||||
{
|
||||
camera_log.success("Found camera: name=%s, description=%s", cameraInfo.deviceName().toStdString(), cameraInfo.description().toStdString());
|
||||
}
|
||||
|
||||
// Let's use the default camera for now
|
||||
set_camera(QCameraInfo::defaultCamera());
|
||||
|
||||
camera_log.notice("Loading camera");
|
||||
|
||||
if (!m_camera)
|
||||
{
|
||||
camera_log.error("No camera found");
|
||||
m_state = camera_handler_state::closed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_camera->state() != QCamera::State::UnloadedState)
|
||||
{
|
||||
camera_log.notice("Camera already loaded");
|
||||
}
|
||||
|
||||
// Load/open camera
|
||||
m_camera->load();
|
||||
|
||||
// List all supported formats for debugging
|
||||
for (const QCamera::FrameRateRange& frame_rate : m_camera->supportedViewfinderFrameRateRanges())
|
||||
{
|
||||
camera_log.notice("Supported frame rate range: %f-%f", frame_rate.minimumFrameRate, frame_rate.maximumFrameRate);
|
||||
}
|
||||
for (const QVideoFrame::PixelFormat& pixel_format : m_camera->supportedViewfinderPixelFormats())
|
||||
{
|
||||
camera_log.notice("Supported pixel format: %d", static_cast<int>(pixel_format));
|
||||
}
|
||||
for (const QSize& resolution : m_camera->supportedViewfinderResolutions())
|
||||
{
|
||||
camera_log.notice("Supported resolution: %dx%d", resolution.width(), resolution.height());
|
||||
}
|
||||
|
||||
// Update camera and view finder settings
|
||||
update_camera_settings();
|
||||
|
||||
m_state = camera_handler_state::open;
|
||||
}
|
||||
|
||||
void qt_camera_handler::close_camera()
|
||||
{
|
||||
camera_log.notice("Unloading camera");
|
||||
|
||||
if (!m_camera)
|
||||
{
|
||||
camera_log.error("No camera found");
|
||||
m_state = camera_handler_state::closed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_camera->state() == QCamera::State::UnloadedState)
|
||||
{
|
||||
camera_log.notice("Camera already unloaded");
|
||||
}
|
||||
|
||||
// Unload/close camera
|
||||
m_camera->unload();
|
||||
m_state = camera_handler_state::closed;
|
||||
}
|
||||
|
||||
void qt_camera_handler::start_camera()
|
||||
{
|
||||
camera_log.notice("Starting camera");
|
||||
|
||||
if (!m_camera)
|
||||
{
|
||||
camera_log.error("No camera found");
|
||||
m_state = camera_handler_state::closed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_camera->state() == QCamera::State::ActiveState)
|
||||
{
|
||||
camera_log.notice("Camera already started");
|
||||
}
|
||||
else if (m_camera->state() == QCamera::State::UnloadedState)
|
||||
{
|
||||
camera_log.notice("Camera not open");
|
||||
open_camera();
|
||||
}
|
||||
|
||||
// Start camera. We will start receiving frames now.
|
||||
m_camera->start();
|
||||
m_state = camera_handler_state::running;
|
||||
}
|
||||
|
||||
void qt_camera_handler::stop_camera()
|
||||
{
|
||||
camera_log.notice("Stopping camera");
|
||||
|
||||
if (!m_camera)
|
||||
{
|
||||
camera_log.error("No camera found");
|
||||
m_state = camera_handler_state::closed;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_camera->state() == QCamera::State::LoadedState)
|
||||
{
|
||||
camera_log.notice("Camera already stopped");
|
||||
}
|
||||
|
||||
// Stop camera. The camera will still be drawing power.
|
||||
m_camera->stop();
|
||||
m_state = camera_handler_state::open;
|
||||
}
|
||||
|
||||
void qt_camera_handler::set_format(s32 format, u32 bytes_per_pixel)
|
||||
{
|
||||
m_format = format;
|
||||
m_bytes_per_pixel = bytes_per_pixel;
|
||||
|
||||
update_camera_settings();
|
||||
}
|
||||
|
||||
void qt_camera_handler::set_frame_rate(u32 frame_rate)
|
||||
{
|
||||
m_frame_rate = frame_rate;
|
||||
|
||||
update_camera_settings();
|
||||
}
|
||||
|
||||
void qt_camera_handler::set_resolution(u32 width, u32 height)
|
||||
{
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
|
||||
update_camera_settings();
|
||||
}
|
||||
|
||||
void qt_camera_handler::set_mirrored(bool mirrored)
|
||||
{
|
||||
m_mirrored = mirrored;
|
||||
|
||||
update_camera_settings();
|
||||
}
|
||||
|
||||
u64 qt_camera_handler::frame_number() const
|
||||
{
|
||||
return m_surface ? m_surface->frame_number() : 0;
|
||||
}
|
||||
|
||||
bool qt_camera_handler::get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read)
|
||||
{
|
||||
width = 0;
|
||||
height = 0;
|
||||
frame_number = 0;
|
||||
bytes_read = 0;
|
||||
|
||||
// Check for errors
|
||||
if (!m_camera || !m_surface || m_state != camera_handler_state::running)
|
||||
{
|
||||
camera_log.error("Error: camera invalid");
|
||||
m_state = camera_handler_state::closed;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (QCamera::Error error = m_camera->error(); error != QCamera::NoError)
|
||||
{
|
||||
camera_log.error("Error: \"%s\" (error=%d)", m_camera ? m_camera->errorString().toStdString() : "", static_cast<int>(error));
|
||||
m_state = camera_handler_state::closed;
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (QCamera::Status status = m_camera->status())
|
||||
{
|
||||
case QCamera::UnavailableStatus:
|
||||
case QCamera::UnloadedStatus:
|
||||
case QCamera::UnloadingStatus:
|
||||
camera_log.error("Camera not open. State=%d", static_cast<int>(status));
|
||||
m_state = camera_handler_state::closed;
|
||||
return false;
|
||||
case QCamera::LoadedStatus:
|
||||
case QCamera::StandbyStatus:
|
||||
case QCamera::StoppingStatus:
|
||||
camera_log.error("Camera not active. State=%d", static_cast<int>(status));
|
||||
m_state = camera_handler_state::open;
|
||||
return false;
|
||||
case QCamera::LoadingStatus:
|
||||
case QCamera::StartingStatus:
|
||||
case QCamera::ActiveStatus:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Copy latest image into out buffer.
|
||||
m_surface->get_image(buf, size, width, height, frame_number, bytes_read);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void qt_camera_handler::update_camera_settings()
|
||||
{
|
||||
// Update camera if possible. We can only do this if it is already loaded.
|
||||
if (m_camera && m_camera->state() != QCamera::State::UnloadedState)
|
||||
{
|
||||
// List all available settings in a cascading fashion and choose the proper value if possible.
|
||||
// After each step, the next one will only list the settings that are compatible with the prior ones.
|
||||
QCameraViewfinderSettings settings;
|
||||
|
||||
// Set resolution if possible.
|
||||
for (const QSize& resolution : m_camera->supportedViewfinderResolutions(settings))
|
||||
{
|
||||
if (m_width == resolution.width() && m_height == resolution.height())
|
||||
{
|
||||
settings.setResolution(resolution.width(), resolution.height());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set frame rate if possible.
|
||||
for (const QCamera::FrameRateRange& frame_rate : m_camera->supportedViewfinderFrameRateRanges(settings))
|
||||
{
|
||||
// Some cameras do not have an exact match, so let's approximate.
|
||||
if (static_cast<qreal>(m_frame_rate) >= (frame_rate.maximumFrameRate - 0.5) && static_cast<qreal>(m_frame_rate) <= (frame_rate.maximumFrameRate + 0.5))
|
||||
{
|
||||
// Lock the frame rate by setting the min and max to the same value.
|
||||
settings.setMinimumFrameRate(m_frame_rate);
|
||||
settings.setMaximumFrameRate(m_frame_rate);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Set pixel format if possible. (Unused for now, because formats differ between Qt and cell)
|
||||
//for (const QVideoFrame::PixelFormat& pixel_format : m_camera->supportedViewfinderPixelFormats(settings))
|
||||
//{
|
||||
// if (pixel_format matches m_format)
|
||||
// {
|
||||
// settings.setPixelFormat(pixel_format);
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
|
||||
camera_log.notice("Setting view finder settings: frame_rate=%f, width=%d, height=%d, pixel_format=%d",
|
||||
settings.maximumFrameRate(), settings.resolution().width(), settings.resolution().height(), static_cast<int>(settings.pixelFormat()));
|
||||
|
||||
// Apply settings.
|
||||
m_camera->setViewfinderSettings(settings);
|
||||
}
|
||||
|
||||
// Update video surface if possible
|
||||
if (m_surface)
|
||||
{
|
||||
m_surface->set_resolution(m_width, m_height);
|
||||
m_surface->set_format(m_format, m_bytes_per_pixel);
|
||||
m_surface->set_mirrored(m_mirrored);
|
||||
}
|
||||
}
|
||||
|
||||
void qt_camera_handler::handle_camera_state(QCamera::State state)
|
||||
{
|
||||
camera_log.notice("Camera state changed to %d", static_cast<int>(state));
|
||||
}
|
||||
|
||||
void qt_camera_handler::handle_camera_status(QCamera::Status status)
|
||||
{
|
||||
camera_log.notice("Camera status changed to %d", static_cast<int>(status));
|
||||
}
|
||||
|
||||
void qt_camera_handler::handle_lock_status(QCamera::LockStatus status, QCamera::LockChangeReason reason)
|
||||
{
|
||||
camera_log.notice("Camera lock status changed to %d (reason=%d)", static_cast<int>(status), static_cast<int>(reason));
|
||||
}
|
||||
|
||||
void qt_camera_handler::handle_capture_modes(QCamera::CaptureModes capture_modes)
|
||||
{
|
||||
camera_log.notice("Camera capture modes changed to %d", static_cast<int>(capture_modes));
|
||||
}
|
||||
|
||||
void qt_camera_handler::handle_camera_error(QCamera::Error error)
|
||||
{
|
||||
camera_log.error("Error: \"%s\" (error=%d)", m_camera ? m_camera->errorString().toStdString() : "", static_cast<int>(error));
|
||||
}
|
41
rpcs3/rpcs3qt/qt_camera_handler.h
Normal file
41
rpcs3/rpcs3qt/qt_camera_handler.h
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include "Emu/Io/camera_handler_base.h"
|
||||
#include "qt_camera_video_surface.h"
|
||||
|
||||
#include <QCamera>
|
||||
#include <QCameraImageCapture>
|
||||
#include <QAbstractVideoSurface>
|
||||
|
||||
class video_surface;
|
||||
|
||||
class qt_camera_handler final : public camera_handler_base, public QObject
|
||||
{
|
||||
public:
|
||||
qt_camera_handler();
|
||||
virtual ~qt_camera_handler();
|
||||
|
||||
void set_camera(const QCameraInfo &cameraInfo);
|
||||
|
||||
void open_camera() override;
|
||||
void close_camera() override;
|
||||
void start_camera() override;
|
||||
void stop_camera() override;
|
||||
void set_format(s32 format, u32 bytes_per_pixel) override;
|
||||
void set_frame_rate(u32 frame_rate) override;
|
||||
void set_resolution(u32 width, u32 height) override;
|
||||
void set_mirrored(bool mirrored) override;
|
||||
u64 frame_number() const override;
|
||||
bool get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read) override;
|
||||
|
||||
private:
|
||||
void handle_lock_status(QCamera::LockStatus, QCamera::LockChangeReason);
|
||||
void handle_capture_modes(QCamera::CaptureModes capture_modes);
|
||||
void handle_camera_state(QCamera::State state);
|
||||
void handle_camera_status(QCamera::Status status);
|
||||
void handle_camera_error(QCamera::Error error);
|
||||
void update_camera_settings();
|
||||
|
||||
std::unique_ptr<QCamera> m_camera;
|
||||
std::unique_ptr<qt_camera_video_surface> m_surface;
|
||||
};
|
262
rpcs3/rpcs3qt/qt_camera_video_surface.cpp
Normal file
262
rpcs3/rpcs3qt/qt_camera_video_surface.cpp
Normal file
@ -0,0 +1,262 @@
|
||||
#include "stdafx.h"
|
||||
#include "qt_camera_video_surface.h"
|
||||
|
||||
#include "Emu/Cell/Modules/cellCamera.h"
|
||||
#include "Emu/system_config.h"
|
||||
|
||||
LOG_CHANNEL(camera_log, "Camera");
|
||||
|
||||
qt_camera_video_surface::qt_camera_video_surface(bool front_facing, QObject *parent)
|
||||
: QAbstractVideoSurface(parent), m_front_facing(front_facing)
|
||||
{
|
||||
}
|
||||
|
||||
qt_camera_video_surface::~qt_camera_video_surface()
|
||||
{
|
||||
std::lock_guard lock(m_mutex);
|
||||
|
||||
// Free memory
|
||||
for (auto& image_buffer : m_image_buffer)
|
||||
{
|
||||
if (image_buffer.data)
|
||||
{
|
||||
delete[] image_buffer.data;
|
||||
image_buffer.data = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<QVideoFrame::PixelFormat> qt_camera_video_surface::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const
|
||||
{
|
||||
Q_UNUSED(type)
|
||||
|
||||
// Let's only allow RGB formats for now
|
||||
QList<QVideoFrame::PixelFormat> result;
|
||||
result
|
||||
<< QVideoFrame::Format_RGB32
|
||||
<< QVideoFrame::Format_RGB24;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool qt_camera_video_surface::present(const QVideoFrame& frame)
|
||||
{
|
||||
if (!frame.isValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get video image
|
||||
QImage image = frame.image();
|
||||
|
||||
if (!image.isNull())
|
||||
{
|
||||
// Scale image if necessary
|
||||
if (m_width > 0 && m_height > 0 && m_width != image.width() && m_height != image.height())
|
||||
{
|
||||
image = image.scaled(m_width, m_height, Qt::AspectRatioMode::KeepAspectRatio, Qt::SmoothTransformation);
|
||||
}
|
||||
|
||||
// Determine image flip
|
||||
const camera_flip flip_setting = g_cfg.io.camera_flip;
|
||||
|
||||
bool flip_horizontally = m_front_facing; // Front facing cameras are flipped already
|
||||
if (flip_setting == camera_flip::horizontal || flip_setting == camera_flip::both)
|
||||
{
|
||||
flip_horizontally = !flip_horizontally;
|
||||
}
|
||||
if (m_mirrored) // Set by the game
|
||||
{
|
||||
flip_horizontally = !flip_horizontally;
|
||||
}
|
||||
|
||||
bool flip_vertically = true; // It appears games expect this. Might be camera specific.
|
||||
if (flip_setting == camera_flip::vertical || flip_setting == camera_flip::both)
|
||||
{
|
||||
flip_vertically = !flip_vertically;
|
||||
}
|
||||
|
||||
// Flip image if necessary
|
||||
if (flip_horizontally || flip_vertically)
|
||||
{
|
||||
image = image.mirrored(flip_horizontally, flip_vertically);
|
||||
}
|
||||
}
|
||||
|
||||
const u64 new_size = m_width * m_height * m_bytes_per_pixel;
|
||||
image_buffer& image_buffer = m_image_buffer[m_write_index];
|
||||
|
||||
// Reset buffer if necessary
|
||||
if (image_buffer.size != new_size)
|
||||
{
|
||||
image_buffer.size = 0;
|
||||
if (image_buffer.data)
|
||||
{
|
||||
delete[] image_buffer.data;
|
||||
image_buffer.data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Create buffer if necessary
|
||||
if (!image_buffer.data && new_size > 0)
|
||||
{
|
||||
image_buffer.data = new u8[new_size];
|
||||
image_buffer.size = new_size;
|
||||
image_buffer.width = m_width;
|
||||
image_buffer.height = m_height;
|
||||
}
|
||||
|
||||
if (image_buffer.size > 0 && !image.isNull())
|
||||
{
|
||||
// Convert image to proper layout
|
||||
// TODO: check if pixel format and bytes per pixel match and convert if necessary
|
||||
// TODO: implement or improve more conversions
|
||||
|
||||
switch (m_format)
|
||||
{
|
||||
case CELL_CAMERA_JPG:
|
||||
break;
|
||||
case CELL_CAMERA_RGBA:
|
||||
break;
|
||||
case CELL_CAMERA_RAW8: // The game seems to expect BGGR
|
||||
{
|
||||
// Let's use a very simple algorithm to convert the image to raw BGGR
|
||||
for (int y = 0; y < std::min<int>(image_buffer.height, image.height()); y++)
|
||||
{
|
||||
for (int x = 0; x < std::min<int>(image_buffer.width, image.width()); x++)
|
||||
{
|
||||
u8& pixel = image_buffer.data[image_buffer.width * y + x];
|
||||
const bool is_left_pixel = (x % 2) == 0;
|
||||
const bool is_top_pixel = (y % 2) == 0;
|
||||
|
||||
if (is_left_pixel && is_top_pixel)
|
||||
{
|
||||
pixel = qBlue(image.pixel(x, y));
|
||||
}
|
||||
else if (is_left_pixel || is_top_pixel)
|
||||
{
|
||||
pixel = qGreen(image.pixel(x, y));
|
||||
}
|
||||
else
|
||||
{
|
||||
pixel = qRed(image.pixel(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
//case CELL_CAMERA_Y0_U_Y1_V:
|
||||
case CELL_CAMERA_YUV422:
|
||||
{
|
||||
// Simple conversion from stackoverflow.
|
||||
const int rgb_bytes_per_pixel = 4;
|
||||
const int yuv_bytes_per_pixel = 2;
|
||||
const int yuv_pitch = image_buffer.width * yuv_bytes_per_pixel;
|
||||
|
||||
for (int y = 0; y < image_buffer.height; y++)
|
||||
{
|
||||
const uint8_t* rgb_row_ptr = image.constScanLine(y);
|
||||
uint8_t* yuv_row_ptr = &image_buffer.data[y * yuv_pitch];
|
||||
|
||||
for (int x = 0; x < image_buffer.width - 1; x += 2)
|
||||
{
|
||||
const int rgb_index = x * rgb_bytes_per_pixel;
|
||||
const int yuv_index = x * yuv_bytes_per_pixel;
|
||||
|
||||
const uint8_t R1 = rgb_row_ptr[rgb_index + 0];
|
||||
const uint8_t G1 = rgb_row_ptr[rgb_index + 1];
|
||||
const uint8_t B1 = rgb_row_ptr[rgb_index + 2];
|
||||
//const uint8_t A1 = rgb_row_ptr[rgb_index + 3];
|
||||
const uint8_t R2 = rgb_row_ptr[rgb_index + 4];
|
||||
const uint8_t G2 = rgb_row_ptr[rgb_index + 5];
|
||||
const uint8_t B2 = rgb_row_ptr[rgb_index + 6];
|
||||
//const uint8_t A2 = rgb_row_ptr[rgb_index + 7];
|
||||
|
||||
const int Y = (0.257f * R1) + (0.504f * G1) + (0.098f * B1) + 16.0f;
|
||||
const int U = -(0.148f * R1) - (0.291f * G1) + (0.439f * B1) + 128.0f;
|
||||
const int V = (0.439f * R1) - (0.368f * G1) - (0.071f * B1) + 128.0f;
|
||||
const int Y2 = (0.257f * R2) + (0.504f * G2) + (0.098f * B2) + 16.0f;
|
||||
|
||||
yuv_row_ptr[yuv_index + 0] = std::max<u8>(0, std::min<u8>(Y, 255));
|
||||
yuv_row_ptr[yuv_index + 1] = std::max<u8>(0, std::min<u8>(U, 255));
|
||||
yuv_row_ptr[yuv_index + 2] = std::max<u8>(0, std::min<u8>(Y2, 255));
|
||||
yuv_row_ptr[yuv_index + 3] = std::max<u8>(0, std::min<u8>(V, 255));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CELL_CAMERA_RAW10:
|
||||
case CELL_CAMERA_YUV420:
|
||||
case CELL_CAMERA_V_Y1_U_Y0:
|
||||
case CELL_CAMERA_FORMAT_UNKNOWN:
|
||||
default:
|
||||
std::memcpy(image_buffer.data, image.constBits(), std::min<usz>(image_buffer.size, image.height() * image.bytesPerLine()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
camera_log.trace("Wrote image to video surface. index=%d, m_frame_number=%d, width=%d, height=%d, bytesPerLine=%d",
|
||||
m_write_index, m_frame_number.load(), image.width(), image.height(), image.bytesPerLine());
|
||||
|
||||
// Toggle write/read index
|
||||
std::lock_guard lock(m_mutex);
|
||||
image_buffer.frame_number = m_frame_number++;
|
||||
m_write_index = read_index();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void qt_camera_video_surface::set_format(s32 format, u32 bytes_per_pixel)
|
||||
{
|
||||
camera_log.notice("Setting format: format=%d, bytes_per_pixel=%d", format, bytes_per_pixel);
|
||||
|
||||
m_format = format;
|
||||
m_bytes_per_pixel = bytes_per_pixel;
|
||||
}
|
||||
|
||||
void qt_camera_video_surface::set_resolution(u32 width, u32 height)
|
||||
{
|
||||
camera_log.notice("Setting resolution: width=%d, height=%d", width, height);
|
||||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
}
|
||||
|
||||
void qt_camera_video_surface::set_mirrored(bool mirrored)
|
||||
{
|
||||
camera_log.notice("Setting mirrored: mirrored=%d", mirrored);
|
||||
|
||||
m_mirrored = mirrored;
|
||||
}
|
||||
|
||||
u64 qt_camera_video_surface::frame_number() const
|
||||
{
|
||||
return m_frame_number.load();
|
||||
}
|
||||
|
||||
void qt_camera_video_surface::get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read)
|
||||
{
|
||||
// Lock read buffer
|
||||
std::lock_guard lock(m_mutex);
|
||||
const image_buffer& image_buffer = m_image_buffer[read_index()];
|
||||
|
||||
width = image_buffer.width;
|
||||
height = image_buffer.height;
|
||||
frame_number = image_buffer.frame_number;
|
||||
|
||||
// Copy to out buffer
|
||||
if (buf && image_buffer.data)
|
||||
{
|
||||
bytes_read = std::min<u64>(image_buffer.size, size);
|
||||
std::memcpy(buf, image_buffer.data, bytes_read);
|
||||
}
|
||||
else
|
||||
{
|
||||
bytes_read = 0;
|
||||
}
|
||||
}
|
||||
|
||||
u32 qt_camera_video_surface::read_index() const
|
||||
{
|
||||
// The read buffer index cannot be the same as the write index
|
||||
return (m_write_index + 1u) % ::narrow<u32>(m_image_buffer.size());
|
||||
}
|
48
rpcs3/rpcs3qt/qt_camera_video_surface.h
Normal file
48
rpcs3/rpcs3qt/qt_camera_video_surface.h
Normal file
@ -0,0 +1,48 @@
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractVideoSurface>
|
||||
#include <QImage>
|
||||
|
||||
#include <array>
|
||||
|
||||
class qt_camera_video_surface final : public QAbstractVideoSurface
|
||||
{
|
||||
public:
|
||||
qt_camera_video_surface(bool front_facing, QObject *parent = nullptr);
|
||||
virtual ~qt_camera_video_surface();
|
||||
|
||||
QList<QVideoFrame::PixelFormat> supportedPixelFormats(QAbstractVideoBuffer::HandleType type = QAbstractVideoBuffer::NoHandle) const override;
|
||||
bool present(const QVideoFrame& frame) override;
|
||||
|
||||
void set_format(s32 format, u32 bytes_per_pixel);
|
||||
void set_resolution(u32 width, u32 height);
|
||||
void set_mirrored(bool mirrored);
|
||||
|
||||
u64 frame_number() const;
|
||||
|
||||
void get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read);
|
||||
|
||||
private:
|
||||
u32 read_index() const;
|
||||
|
||||
bool m_front_facing = false;
|
||||
bool m_mirrored = false; // Set by cellCamera
|
||||
s32 m_format = 2; // CELL_CAMERA_RAW8, set by cellCamera
|
||||
u32 m_bytes_per_pixel = 1;
|
||||
u32 m_width = 640;
|
||||
u32 m_height = 480;
|
||||
|
||||
std::mutex m_mutex;
|
||||
atomic_t<u64> m_frame_number{0};
|
||||
u32 m_write_index{0};
|
||||
|
||||
struct image_buffer
|
||||
{
|
||||
u64 frame_number = 0;
|
||||
u32 width = 0;
|
||||
u32 height = 0;
|
||||
u64 size = 0;
|
||||
u8* data = nullptr;
|
||||
};
|
||||
std::array<image_buffer, 2> m_image_buffer;
|
||||
};
|
@ -21,6 +21,7 @@ screenshot_preview::screenshot_preview(const QString& filepath, QWidget* parent)
|
||||
setWindowTitle(tr("Screenshot Viewer"));
|
||||
setObjectName("screenshot_preview");
|
||||
setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
setAttribute(Qt::WA_DeleteOnClose);
|
||||
setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
|
||||
setPixmap(QPixmap::fromImage(m_image));
|
||||
setMinimumSize(160, 90);
|
||||
|
@ -955,6 +955,9 @@ settings_dialog::settings_dialog(std::shared_ptr<gui_settings> gui_settings, std
|
||||
m_emu_settings->EnhanceComboBox(ui->cameraBox, emu_settings_type::Camera);
|
||||
SubscribeTooltip(ui->gb_camera_setting, tooltips.settings.camera);
|
||||
|
||||
m_emu_settings->EnhanceComboBox(ui->cameraFlipBox, emu_settings_type::CameraFlip);
|
||||
SubscribeTooltip(ui->gb_camera_flip, tooltips.settings.camera_flip);
|
||||
|
||||
m_emu_settings->EnhanceComboBox(ui->moveBox, emu_settings_type::Move);
|
||||
SubscribeTooltip(ui->gb_move_handler, tooltips.settings.move);
|
||||
|
||||
|
@ -1494,7 +1494,7 @@
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="inputTabLayout3" stretch="1,1,1">
|
||||
<layout class="QHBoxLayout" name="inputTabLayout3">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="gb_turntable_emulated">
|
||||
<property name="title">
|
||||
@ -1520,7 +1520,16 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QWidget" name="inputTabSpacerWidget" native="true"/>
|
||||
<widget class="QGroupBox" name="gb_camera_flip">
|
||||
<property name="title">
|
||||
<string>Camera Flip</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="gb_camera_flip_layout">
|
||||
<item>
|
||||
<widget class="QComboBox" name="cameraFlipBox"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
|
@ -192,8 +192,9 @@ public:
|
||||
const QString pad_handler_linux = tr("If you want to use the keyboard to control, select the Keyboard option.\nIf you have a DualShock 4, select DualShock 4.");
|
||||
const QString keyboard_handler = tr("Some games support native keyboard input.\nBasic will work in these cases.");
|
||||
const QString mouse_handler = tr("Some games support native mouse input.\nBasic will work in these cases.");
|
||||
const QString camera = tr("Camera support is not implemented, leave this on null.");
|
||||
const QString camera_type = tr("Camera support is not implemented, leave this on unknown.");
|
||||
const QString camera = tr("Select Qt Camera to use the default camera device of your operating system.");
|
||||
const QString camera_type = tr("Depending on the game, you may need to select a specific camera type.");
|
||||
const QString camera_flip = tr("Flips the camera image either horizontally, vertically, or on both axis.");
|
||||
const QString move = tr("PlayStation Move support.\nFake: Experimental! This maps Move controls to DS3 controller mappings.\nMouse: Emulate PSMove with Mouse handler.");
|
||||
const QString buzz = tr("Buzz! support.\nSelect 1 or 2 controllers if the game requires Buzz! controllers and you don't have real controllers.\nSelect Null if the game has support for DualShock or if you have real Buzz! controllers.");
|
||||
const QString turntable = tr("DJ Hero Turntable controller support.\nSelect 1 or 2 controllers if the game requires DJ Hero Turntable controllers and you don't have real turntable controllers.\nSelect Null if the game has support for DualShock or if you have real turntable controllers.\nA real turntable controller can be used at the same time as an emulated turntable controller.");
|
||||
|
Loading…
Reference in New Issue
Block a user