cellCamera: Add qt camera handler

This commit is contained in:
Megamouse 2021-10-13 20:18:18 +02:00
parent ee7ed1fdc3
commit 08011e9b78
26 changed files with 1375 additions and 165 deletions

8
3rdparty/qt5.cmake vendored
View File

@ -2,17 +2,17 @@ add_library(3rdparty_qt5 INTERFACE)
set(QT_MIN_VER 5.15.2) 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) if(WIN32)
find_package(Qt5 ${QT_MIN_VER} COMPONENTS WinExtras REQUIRED) 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() else()
find_package(Qt5 ${QT_MIN_VER} COMPONENTS DBus Gui) find_package(Qt5 ${QT_MIN_VER} COMPONENTS DBus Gui)
if(Qt5DBus_FOUND) 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) target_compile_definitions(3rdparty_qt5 INTERFACE -DHAVE_QTDBUS)
else() else()
target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Concurrent) target_link_libraries(3rdparty_qt5 INTERFACE Qt5::Widgets Qt5::Concurrent Qt5::Multimedia)
endif() endif()
target_include_directories(3rdparty_qt5 INTERFACE ${Qt5Gui_PRIVATE_INCLUDE_DIRS}) target_include_directories(3rdparty_qt5 INTERFACE ${Qt5Gui_PRIVATE_INCLUDE_DIRS})
endif() endif()

View File

@ -115,7 +115,8 @@ static bool check_dev_num(s32 dev_num)
return dev_num == 0; 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: // TODO: I managed to get 0x80990004 once. :thonkang:
@ -263,14 +264,88 @@ u32 get_video_buffer_size(const CellCameraInfoEx& info)
u32 width, height; u32 width, height;
std::tie(width, height) = get_video_resolution(info); std::tie(width, height) = get_video_resolution(info);
const auto bpp = 4; u32 bytes_per_pixel;
return width * height * bpp;
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 * // * 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() error_code cellCameraInit()
{ {
cellCamera.todo("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. // 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; g_camera.init = 1;
return CELL_OK; return CELL_OK;
} }
@ -359,11 +430,7 @@ error_code cellCameraEnd()
return CELL_CAMERA_ERROR_NOT_INIT; return CELL_CAMERA_ERROR_NOT_INIT;
} }
// TODO: My tests hinted to this behavior, but I'm not sure, so I'll leave this commented // TODO: call cellCameraClose(0), ignore errors
//if (auto res = cellCameraClose(0))
//{
// return res;
//}
// TODO // TODO
g_camera.init = 0; g_camera.init = 0;
@ -371,9 +438,42 @@ error_code cellCameraEnd()
return CELL_OK; 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; return CELL_OK;
} }
@ -385,7 +485,7 @@ error_code cellCameraOpenAsync()
error_code cellCameraOpenEx(s32 dev_num, vm::ptr<CellCameraInfoEx> info) 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 // 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); 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.is_open = true;
g_camera.info = *info; 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>(); auto& shared_data = g_fxo->get<gem_camera_shared>();
shared_data.width = info->width > 0 ? +info->width : 640; shared_data.width = info->width > 0 ? +info->width : 640;
shared_data.height = info->height > 0 ? +info->height : 480; 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); 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; return CELL_CAMERA_ERROR_PARAM;
} }
if (error_code error = check_resolution(dev_num))
{
return error;
}
auto& g_camera = g_fxo->get<camera_thread>(); 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); 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; g_camera.is_open = false;
return CELL_OK; return CELL_OK;
@ -509,7 +643,7 @@ error_code cellCameraClosePost()
error_code cellCameraGetDeviceGUID(s32 dev_num, vm::ptr<u32> guid) 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>(); 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; return CELL_CAMERA_ERROR_NOT_INIT;
} }
// Does not check params or is_open (maybe attached?) if (guid)
{
*guid = 0; // apparently always 0 *guid = 0; // apparently always 0
}
return CELL_OK; 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); 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; return CELL_CAMERA_ERROR_PARAM;
} }
if (error_code error = check_resolution(dev_num))
{
return error;
}
if (!g_camera.is_attached) if (!g_camera.is_attached)
{ {
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND; return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
@ -571,16 +711,16 @@ s32 cellCameraIsAvailable(s32 dev_num)
return false; 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; 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; return true;
@ -607,11 +747,18 @@ s32 cellCameraIsAttached(s32 dev_num)
return false; return false;
} }
vm::var<s32> type;
if (cellCameraGetType(dev_num, type) != CELL_OK)
{
return false;
}
std::lock_guard lock(g_camera.mutex); std::lock_guard lock(g_camera.mutex);
bool is_attached = g_camera.is_attached; 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 // "attach" camera here
// normally should be attached immediately after event queue is registered, but just to be sure // 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) 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) if (g_cfg.io.camera == camera_handler::null)
{ {
@ -653,7 +800,7 @@ s32 cellCameraIsOpen(s32 dev_num)
s32 cellCameraIsStarted(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) 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; return CELL_CAMERA_ERROR_NOT_INIT;
} }
if (g_cfg.io.camera == camera_handler::null) if (!check_dev_num(dev_num))
{
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
{ {
return CELL_CAMERA_ERROR_PARAM; 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 // actually compares <= 0x63 which is equivalent
if (attrib < CELL_CAMERA_FORMATCAP && !g_camera.is_open) if (attrib < CELL_CAMERA_FORMATCAP && !g_camera.is_open)
{ {
return CELL_CAMERA_ERROR_NOT_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); std::lock_guard lock(g_camera.mutex);
if (!g_camera.is_attached) 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; return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
} }
if (!attr_name) // invalid attributes don't have a name
{
return CELL_CAMERA_ERROR_PARAM;
}
if (arg1) if (arg1)
{ {
*arg1 = g_camera.attr[attrib].v1; *arg1 = g_camera.attr[attrib].v1;
} }
if (arg2) if (arg2)
{ {
*arg2 = g_camera.attr[attrib].v2; *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; 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; 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) if (g_cfg.io.camera == camera_handler::null)
{ {
return not_an_error(CELL_CAMERA_ERROR_NOT_OPEN); 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 // actually compares <= 0x63 which is equivalent
if (attrib < CELL_CAMERA_FORMATCAP && !g_camera.is_open) if (attrib < CELL_CAMERA_FORMATCAP && !g_camera.is_open)
{ {
return CELL_CAMERA_ERROR_NOT_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); g_camera.set_attr(attrib, arg1, arg2);
return CELL_OK; 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); 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)) if (!check_dev_num(dev_num))
{ {
return CELL_CAMERA_ERROR_PARAM; return CELL_CAMERA_ERROR_PARAM;
@ -811,29 +983,24 @@ error_code cellCameraGetBufferSize(s32 dev_num, vm::ptr<CellCameraInfoEx> info)
return status; return status;
} }
if (error_code error = check_resolution(dev_num))
{
return error;
}
std::lock_guard lock(g_camera.mutex); std::lock_guard lock(g_camera.mutex);
info->bytesize = get_video_buffer_size(g_camera.info);
g_camera.info = *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>(); auto& g_camera = g_fxo->get<camera_thread>();
if (!g_camera.init) if (!g_camera.init)
@ -841,27 +1008,70 @@ error_code cellCameraGetBufferInfoEx(s32 dev_num, vm::ptr<CellCameraInfoEx> info
return CELL_CAMERA_ERROR_NOT_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 (!check_dev_num(dev_num)) if (!check_dev_num(dev_num))
{ {
return CELL_CAMERA_ERROR_PARAM; 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) if (!g_camera.is_open)
{ {
return CELL_CAMERA_ERROR_NOT_OPEN; return CELL_CAMERA_ERROR_NOT_OPEN;
} }
if (!info) if (!is_valid_info_struct)
{ {
return CELL_CAMERA_ERROR_PARAM; 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); 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; *info = g_camera.info;
return CELL_OK; 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) 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; return CELL_OK;
} }
error_code cellCameraCtrlExtensionUnit(s32 dev_num, u8 request, u16 value, u16 length, vm::ptr<u8> data) 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; return CELL_OK;
} }
@ -903,28 +1156,18 @@ error_code cellCameraReset(s32 dev_num)
{ {
cellCamera.todo("cellCameraReset(dev_num=%d)", 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>(); 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) if (!g_camera.is_attached)
{ {
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND; 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); 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>(); 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) if (!g_camera.is_attached)
{ {
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND; 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.start_timestamp = get_guest_system_time();
g_camera.is_streaming = true; 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) 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>({}); 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) 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>(); 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); 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; return CELL_OK;
} }
@ -1093,28 +1386,18 @@ error_code cellCameraStop(s32 dev_num)
{ {
cellCamera.todo("cellCameraStop(dev_num=%d)", 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>(); 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) if (!g_camera.is_attached)
{ {
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND; return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
@ -1125,6 +1408,23 @@ error_code cellCameraStop(s32 dev_num)
return CELL_CAMERA_ERROR_NOT_STARTED; 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; g_camera.is_streaming = false;
return CELL_OK; return CELL_OK;
@ -1158,6 +1458,11 @@ error_code cellCameraSetNotifyEventQueue(u64 key)
return CELL_OK; return CELL_OK;
} }
if (error_code error = check_resolution(0))
{
return error;
}
g_camera.add_queue(key, 0, 0); g_camera.add_queue(key, 0, 0);
return CELL_OK; return CELL_OK;
@ -1179,6 +1484,11 @@ error_code cellCameraRemoveNotifyEventQueue(u64 key)
return CELL_OK; return CELL_OK;
} }
if (error_code error = check_resolution(0))
{
return error;
}
g_camera.remove_queue(key); g_camera.remove_queue(key);
return CELL_OK; 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); 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>(); auto& g_camera = g_fxo->get<camera_thread>();
if (!g_camera.init) if (!g_camera.init)
@ -1200,6 +1505,16 @@ error_code cellCameraSetNotifyEventQueue2(u64 key, u64 source, u64 flag)
return CELL_CAMERA_ERROR_NOT_INIT; 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); g_camera.add_queue(key, source, flag);
return CELL_OK; return CELL_OK;
@ -1341,6 +1656,7 @@ void camera_context::reset_state()
std::scoped_lock lock(mutex_notify_data_map); std::scoped_lock lock(mutex_notify_data_map);
notify_data_map.clear(); notify_data_map.clear();
handler.reset();
} }
void camera_context::send_attach_state(bool attached) 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) 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) 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; arg1 = CELL_CAMERA_READ_FUNCCALL;
} }
read_mode.exchange(arg1); read_mode.exchange(arg1);
break;
}
case CELL_CAMERA_MIRRORFLAG:
{
if (handler)
{
handler->set_mirrored(!!arg1);
}
break;
}
default:
break;
} }
std::lock_guard lock(mutex); std::lock_guard lock(mutex);

View File

@ -3,6 +3,7 @@
#include "Utilities/Timer.h" #include "Utilities/Timer.h"
#include "Emu/Cell/lv2/sys_memory.h" #include "Emu/Cell/lv2/sys_memory.h"
#include "Utilities/Thread.h" #include "Utilities/Thread.h"
#include "Emu/Io/camera_handler_base.h"
#include <map> #include <map>
@ -436,6 +437,8 @@ public:
atomic_t<u32> init = 0; atomic_t<u32> init = 0;
static constexpr auto thread_name = "Camera Thread"sv; static constexpr auto thread_name = "Camera Thread"sv;
std::shared_ptr<camera_handler_base> handler;
}; };
using camera_thread = named_thread<camera_context>; using camera_thread = named_thread<camera_context>;

View 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;
}
};

View 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;
};

View File

@ -635,7 +635,7 @@ game_boot_result Emulator::Load(const std::string& title_id, bool add_only, bool
if (!add_only) 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 }) if (fs::file cfg_file{ m_config_path })
{ {

View File

@ -70,6 +70,7 @@ struct EmuCallbacks
std::function<void(std::string_view title_id)> init_pad_handler; std::function<void(std::string_view title_id)> init_pad_handler;
std::function<std::unique_ptr<class GSFrameBase>()> get_gs_frame; std::function<std::unique_ptr<class GSFrameBase>()> get_gs_frame;
std::function<void()> init_gs_render; 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 AudioBackend>()> get_audio;
std::function<std::shared_ptr<class MsgDialogBase>()> get_msg_dialog; std::function<std::shared_ptr<class MsgDialogBase>()> get_msg_dialog;
std::function<std::shared_ptr<class OskDialogBase>()> get_osk_dialog; std::function<std::shared_ptr<class OskDialogBase>()> get_osk_dialog;

View File

@ -243,6 +243,7 @@ struct cfg_root : cfg::node
cfg::_enum<mouse_handler> mouse{ this, "Mouse", mouse_handler::basic }; cfg::_enum<mouse_handler> mouse{ this, "Mouse", mouse_handler::basic };
cfg::_enum<camera_handler> camera{ this, "Camera", camera_handler::null }; 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<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<move_handler> move{ this, "Move", move_handler::null };
cfg::_enum<buzz_handler> buzz{ this, "Buzz emulated controller", buzz_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}; cfg::_enum<turntable_handler> turntable{this, "Turntable emulated controller", turntable_handler::null};

View File

@ -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::null: return "Null";
case camera_handler::fake: return "Fake"; case camera_handler::fake: return "Fake";
case camera_handler::qt: return "Qt";
} }
return unknown; 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 <> template <>
void fmt_class_string<move_handler>::format(std::string& out, u64 arg) void fmt_class_string<move_handler>::format(std::string& out, u64 arg)
{ {

View File

@ -78,6 +78,15 @@ enum class camera_handler
{ {
null, null,
fake, fake,
qt
};
enum class camera_flip
{
none,
horizontal,
vertical,
both
}; };
enum class fake_camera_type enum class fake_camera_type

View File

@ -445,6 +445,8 @@
<ClInclude Include="Emu\Cell\Modules\cellStorage.h" /> <ClInclude Include="Emu\Cell\Modules\cellStorage.h" />
<ClInclude Include="Emu\Cell\Modules\libfs_utility_init.h" /> <ClInclude Include="Emu\Cell\Modules\libfs_utility_init.h" />
<ClInclude Include="Emu\Cell\Modules\sys_crashdump.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\Turntable.h" />
<ClInclude Include="Emu\Io\GHLtar.h" /> <ClInclude Include="Emu\Io\GHLtar.h" />
<ClInclude Include="Emu\Io\Buzz.h" /> <ClInclude Include="Emu\Io\Buzz.h" />
@ -458,15 +460,15 @@
<ClInclude Include="Emu\NP\generated\np2_structs_generated.h" /> <ClInclude Include="Emu\NP\generated\np2_structs_generated.h" />
<ClInclude Include="Emu\NP\np_handler.h" /> <ClInclude Include="Emu\NP\np_handler.h" />
<ClInclude Include="Emu\NP\signaling_handler.h" /> <ClInclude Include="Emu\NP\signaling_handler.h" />
<ClInclude Include="Emu\NP\np_structs_extra.h" /> <ClInclude Include="Emu\NP\np_structs_extra.h" />
<ClInclude Include="Emu\NP\rpcn_client.h" /> <ClInclude Include="Emu\NP\rpcn_client.h" />
<ClInclude Include="Emu\NP\rpcn_config.h" /> <ClInclude Include="Emu\NP\rpcn_config.h" />
<ClInclude Include="Emu\RSX\Common\bitfield.hpp" /> <ClInclude Include="Emu\RSX\Common\bitfield.hpp" />
<ClInclude Include="Emu\RSX\Common\profiling_timer.hpp" /> <ClInclude Include="Emu\RSX\Common\profiling_timer.hpp" />
<ClInclude Include="Emu\RSX\Common\simple_array.hpp" /> <ClInclude Include="Emu\RSX\Common\simple_array.hpp" />
<ClInclude Include="Emu\RSX\Overlays\overlay_edit_text.hpp" /> <ClInclude Include="Emu\RSX\Overlays\overlay_edit_text.hpp" />
<ClInclude Include="Emu\RSX\Overlays\overlay_list_view.hpp" /> <ClInclude Include="Emu\RSX\Overlays\overlay_list_view.hpp" />
<ClInclude Include="Emu\RSX\Overlays\overlay_progress_bar.hpp" /> <ClInclude Include="Emu\RSX\Overlays\overlay_progress_bar.hpp" />
<ClInclude Include="Emu\RSX\Program\GLSLTypes.h" /> <ClInclude Include="Emu\RSX\Program\GLSLTypes.h" />
<ClInclude Include="Emu\RSX\Program\ProgramStateCache.h" /> <ClInclude Include="Emu\RSX\Program\ProgramStateCache.h" />
<ClInclude Include="Emu\RSX\Program\program_util.h" /> <ClInclude Include="Emu\RSX\Program\program_util.h" />

View File

@ -1999,6 +1999,12 @@
<ClInclude Include="Emu\RSX\Common\bitfield.hpp"> <ClInclude Include="Emu\RSX\Common\bitfield.hpp">
<Filter>Emu\GPU\RSX\Common</Filter> <Filter>Emu\GPU\RSX\Common</Filter>
</ClInclude> </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>
<ItemGroup> <ItemGroup>
<None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl"> <None Include="Emu\RSX\Common\Interpreter\FragmentInterpreter.glsl">

View File

@ -71,7 +71,7 @@
</PropertyGroup> </PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile> <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> <AdditionalOptions>-Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
<AssemblerListingLocation>release\</AssemblerListingLocation> <AssemblerListingLocation>release\</AssemblerListingLocation>
<BrowseInformation>false</BrowseInformation> <BrowseInformation>false</BrowseInformation>
@ -79,7 +79,7 @@
<DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings> <DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ObjectFileName>$(IntDir)</ObjectFileName> <ObjectFileName>$(IntDir)</ObjectFileName>
<Optimization>MaxSpeed</Optimization> <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> <PreprocessToFile>false</PreprocessToFile>
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName> <ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
<RuntimeTypeInfo>true</RuntimeTypeInfo> <RuntimeTypeInfo>true</RuntimeTypeInfo>
@ -88,7 +88,7 @@
<WarningLevel>Level3</WarningLevel> <WarningLevel>Level3</WarningLevel>
</ClCompile> </ClCompile>
<Link> <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> <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> <AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions)</AdditionalOptions>
<DataExecutionPrevention>true</DataExecutionPrevention> <DataExecutionPrevention>true</DataExecutionPrevention>
@ -122,7 +122,7 @@
</ItemDefinitionGroup> </ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile> <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> <AdditionalOptions>-Zc:strictStrings -Zc:throwingNew- -w34100 -w34189 -w44996 -w44456 -w44457 -w44458 %(AdditionalOptions)</AdditionalOptions>
<AssemblerListingLocation>debug\</AssemblerListingLocation> <AssemblerListingLocation>debug\</AssemblerListingLocation>
<BrowseInformation>false</BrowseInformation> <BrowseInformation>false</BrowseInformation>
@ -130,7 +130,7 @@
<DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings> <DisableSpecificWarnings>4577;4467;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ObjectFileName>$(IntDir)</ObjectFileName> <ObjectFileName>$(IntDir)</ObjectFileName>
<Optimization>Disabled</Optimization> <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> <PreprocessToFile>false</PreprocessToFile>
<RuntimeTypeInfo>true</RuntimeTypeInfo> <RuntimeTypeInfo>true</RuntimeTypeInfo>
<SuppressStartupBanner>true</SuppressStartupBanner> <SuppressStartupBanner>true</SuppressStartupBanner>
@ -139,7 +139,7 @@
<ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName> <ProgramDataBaseFileName>$(IntDir)vc$(PlatformToolsetVersion).pdb</ProgramDataBaseFileName>
</ClCompile> </ClCompile>
<Link> <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> <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> <AdditionalOptions>"/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" /VERBOSE %(AdditionalOptions)</AdditionalOptions>
<DataExecutionPrevention>true</DataExecutionPrevention> <DataExecutionPrevention>true</DataExecutionPrevention>
@ -594,6 +594,7 @@
<ClCompile Include="rpcs3qt\patch_manager_dialog.cpp" /> <ClCompile Include="rpcs3qt\patch_manager_dialog.cpp" />
<ClCompile Include="rpcs3qt\pkg_install_dialog.cpp" /> <ClCompile Include="rpcs3qt\pkg_install_dialog.cpp" />
<ClCompile Include="rpcs3qt\persistent_settings.cpp" /> <ClCompile Include="rpcs3qt\persistent_settings.cpp" />
<ClCompile Include="rpcs3qt\qt_camera_handler.cpp" />
<ClCompile Include="rpcs3qt\recvmessage_dialog_frame.cpp" /> <ClCompile Include="rpcs3qt\recvmessage_dialog_frame.cpp" />
<ClCompile Include="rpcs3qt\render_creator.cpp" /> <ClCompile Include="rpcs3qt\render_creator.cpp" />
<ClCompile Include="rpcs3qt\rpcn_settings_dialog.cpp" /> <ClCompile Include="rpcs3qt\rpcn_settings_dialog.cpp" />
@ -604,6 +605,7 @@
<ClCompile Include="rpcs3qt\skylander_dialog.cpp" /> <ClCompile Include="rpcs3qt\skylander_dialog.cpp" />
<ClCompile Include="rpcs3qt\tooltips.cpp" /> <ClCompile Include="rpcs3qt\tooltips.cpp" />
<ClCompile Include="rpcs3qt\update_manager.cpp" /> <ClCompile Include="rpcs3qt\update_manager.cpp" />
<ClCompile Include="rpcs3qt\qt_camera_video_surface.cpp" />
<ClCompile Include="rpcs3qt\_discord_utils.cpp" /> <ClCompile Include="rpcs3qt\_discord_utils.cpp" />
<ClCompile Include="rpcs3qt\find_dialog.cpp" /> <ClCompile Include="rpcs3qt\find_dialog.cpp" />
<ClCompile Include="rpcs3qt\game_compatibility.cpp" /> <ClCompile Include="rpcs3qt\game_compatibility.cpp" />
@ -1088,6 +1090,7 @@
<Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs> <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> <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> </CustomBuild>
<ClInclude Include="rpcs3qt\qt_camera_handler.h" />
<ClInclude Include="rpcs3qt\richtext_item_delegate.h" /> <ClInclude Include="rpcs3qt\richtext_item_delegate.h" />
<CustomBuild Include="rpcs3qt\sendmessage_dialog_frame.h"> <CustomBuild Include="rpcs3qt\sendmessage_dialog_frame.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs> <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> <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> <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> </CustomBuild>
<ClInclude Include="rpcs3qt\qt_camera_video_surface.h" />
<ClInclude Include="rpcs3qt\_discord_utils.h" /> <ClInclude Include="rpcs3qt\_discord_utils.h" />
<CustomBuild Include="rpcs3qt\find_dialog.h"> <CustomBuild Include="rpcs3qt\find_dialog.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs> <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>

View File

@ -133,6 +133,9 @@
<Filter Include="Gui\rpcn"> <Filter Include="Gui\rpcn">
<UniqueIdentifier>{dfd401ec-dd22-4f8a-9dea-565a03efab6a}</UniqueIdentifier> <UniqueIdentifier>{dfd401ec-dd22-4f8a-9dea-565a03efab6a}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Io\camera">
<UniqueIdentifier>{f1996891-48b7-441a-b3fe-52794cbebe80}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="main.cpp"> <ClCompile Include="main.cpp">
@ -777,6 +780,12 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_recvmessage_dialog_frame.cpp"> <ClCompile Include="QTGeneratedFiles\Release\moc_recvmessage_dialog_frame.cpp">
<Filter>Generated Files\Release</Filter> <Filter>Generated Files\Release</Filter>
</ClCompile> </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>
<ItemGroup> <ItemGroup>
<ClInclude Include="Input\ds4_pad_handler.h"> <ClInclude Include="Input\ds4_pad_handler.h">
@ -911,6 +920,12 @@
<ClInclude Include="QTGeneratedFiles\ui_patch_creator_dialog.h"> <ClInclude Include="QTGeneratedFiles\ui_patch_creator_dialog.h">
<Filter>Generated Files</Filter> <Filter>Generated Files</Filter>
</ClInclude> </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>
<ItemGroup> <ItemGroup>
<CustomBuild Include="debug\moc_predefs.h.cbt"> <CustomBuild Include="debug\moc_predefs.h.cbt">

View File

@ -49,6 +49,8 @@ set(SRC_FILES
persistent_settings.cpp persistent_settings.cpp
pkg_install_dialog.cpp pkg_install_dialog.cpp
progress_dialog.cpp progress_dialog.cpp
qt_camera_handler.cpp
qt_camera_video_surface.cpp
qt_utils.cpp qt_utils.cpp
register_editor_dialog.cpp register_editor_dialog.cpp
recvmessage_dialog_frame.cpp recvmessage_dialog_frame.cpp

View File

@ -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"); case fake_camera_type::uvc1_1: return tr("UVC 1.1", "Camera type");
} }
break; 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: case emu_settings_type::Camera:
switch (static_cast<camera_handler>(index)) switch (static_cast<camera_handler>(index))
{ {
case camera_handler::null: return tr("Null", "Camera handler"); case camera_handler::null: return tr("Null", "Camera handler");
case camera_handler::fake: return tr("Fake", "Camera handler"); case camera_handler::fake: return tr("Fake", "Camera handler");
case camera_handler::qt: return tr("Qt", "Camera handler");
} }
break; break;
case emu_settings_type::Move: case emu_settings_type::Move:

View File

@ -125,6 +125,7 @@ enum class emu_settings_type
MouseHandler, MouseHandler,
Camera, Camera,
CameraType, CameraType,
CameraFlip,
Move, Move,
Buzz, Buzz,
Turntable, 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::MouseHandler, { "Input/Output", "Mouse"}},
{ emu_settings_type::Camera, { "Input/Output", "Camera"}}, { emu_settings_type::Camera, { "Input/Output", "Camera"}},
{ emu_settings_type::CameraType, { "Input/Output", "Camera type"}}, { 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::Move, { "Input/Output", "Move" }},
{ emu_settings_type::Buzz, { "Input/Output", "Buzz emulated controller" }}, { emu_settings_type::Buzz, { "Input/Output", "Buzz emulated controller" }},
{ emu_settings_type::Turntable, { "Input/Output", "Turntable emulated controller" }}, { emu_settings_type::Turntable, { "Input/Output", "Turntable emulated controller" }},

View File

@ -15,11 +15,13 @@
#include "gl_gs_frame.h" #include "gl_gs_frame.h"
#include "display_sleep_control.h" #include "display_sleep_control.h"
#include "localized_emu.h" #include "localized_emu.h"
#include "qt_camera_handler.h"
#ifdef WITH_DISCORD_RPC #ifdef WITH_DISCORD_RPC
#include "_discord_utils.h" #include "_discord_utils.h"
#endif #endif
#include "Emu/Io/Null/null_camera_handler.h"
#include "Emu/Cell/Modules/cellAudio.h" #include "Emu/Cell/Modules/cellAudio.h"
#include "Emu/RSX/Overlays/overlay_perf_metrics.h" #include "Emu/RSX/Overlays/overlay_perf_metrics.h"
#include "Emu/system_utils.hpp" #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_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_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; }; callbacks.get_osk_dialog = [this]() -> std::shared_ptr<OskDialogBase> { return m_show_gui ? std::make_shared<osk_dialog_frame>() : nullptr; };

View 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));
}

View 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;
};

View 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());
}

View 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;
};

View File

@ -21,6 +21,7 @@ screenshot_preview::screenshot_preview(const QString& filepath, QWidget* parent)
setWindowTitle(tr("Screenshot Viewer")); setWindowTitle(tr("Screenshot Viewer"));
setObjectName("screenshot_preview"); setObjectName("screenshot_preview");
setContextMenuPolicy(Qt::CustomContextMenu); setContextMenuPolicy(Qt::CustomContextMenu);
setAttribute(Qt::WA_DeleteOnClose);
setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
setPixmap(QPixmap::fromImage(m_image)); setPixmap(QPixmap::fromImage(m_image));
setMinimumSize(160, 90); setMinimumSize(160, 90);

View File

@ -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); m_emu_settings->EnhanceComboBox(ui->cameraBox, emu_settings_type::Camera);
SubscribeTooltip(ui->gb_camera_setting, tooltips.settings.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); m_emu_settings->EnhanceComboBox(ui->moveBox, emu_settings_type::Move);
SubscribeTooltip(ui->gb_move_handler, tooltips.settings.move); SubscribeTooltip(ui->gb_move_handler, tooltips.settings.move);

View File

@ -1494,7 +1494,7 @@
</layout> </layout>
</item> </item>
<item> <item>
<layout class="QHBoxLayout" name="inputTabLayout3" stretch="1,1,1"> <layout class="QHBoxLayout" name="inputTabLayout3">
<item> <item>
<widget class="QGroupBox" name="gb_turntable_emulated"> <widget class="QGroupBox" name="gb_turntable_emulated">
<property name="title"> <property name="title">
@ -1520,7 +1520,16 @@
</widget> </widget>
</item> </item>
<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> </item>
</layout> </layout>
</item> </item>

View File

@ -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 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 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 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 = tr("Select Qt Camera to use the default camera device of your operating system.");
const QString camera_type = tr("Camera support is not implemented, leave this on unknown."); 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 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 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."); 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.");