cellCamera: add error handling to qt camera

This commit is contained in:
Megamouse 2021-10-17 15:35:30 +02:00
parent e4b242955c
commit 81c216f330
12 changed files with 254 additions and 139 deletions

View File

@ -1328,17 +1328,6 @@ error_code cellCameraReadEx(s32 dev_num, vm::ptr<CellCameraReadEx> read)
// can call cellCameraReset() and cellCameraStop() in some cases // can call cellCameraReset() and cellCameraStop() in some cases
if (read) // NULL returns CELL_OK
{
read->timestamp = (get_guest_system_time() - g_camera.start_timestamp);
read->frame = g_camera.frame_num;
read->bytesread = g_camera.is_streaming ? get_video_buffer_size(g_camera.info) : 0;
auto& shared_data = g_fxo->get<gem_camera_shared>();
shared_data.frame_timestamp.exchange(read->timestamp);
}
if (g_camera.handler) if (g_camera.handler)
{ {
u32 width{}; u32 width{};
@ -1347,7 +1336,7 @@ error_code cellCameraReadEx(s32 dev_num, vm::ptr<CellCameraReadEx> read)
u64 bytes_read{}; u64 bytes_read{};
atomic_t<bool> wake_up = false; atomic_t<bool> wake_up = false;
bool result = false; camera_handler_base::camera_handler_state result = camera_handler_base::camera_handler_state::not_available;
Emu.CallAfter([&]() Emu.CallAfter([&]()
{ {
@ -1361,10 +1350,9 @@ error_code cellCameraReadEx(s32 dev_num, vm::ptr<CellCameraReadEx> read)
thread_ctrl::wait_on(wake_up, false); thread_ctrl::wait_on(wake_up, false);
} }
if (!result) if (error_code error = g_camera.on_handler_state(result); error != CELL_OK)
{ {
g_camera.send_attach_state(false); return error;
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
} }
if (read) if (read)
@ -1377,6 +1365,21 @@ error_code cellCameraReadEx(s32 dev_num, vm::ptr<CellCameraReadEx> read)
frame_number, width, height, bytes_read, read ? read->frame.get() : 0, read ? read->bytesread.get() : 0); frame_number, width, height, bytes_read, read ? read->frame.get() : 0, read ? read->bytesread.get() : 0);
} }
if (read) // NULL returns CELL_OK
{
read->timestamp = (get_guest_system_time() - g_camera.start_timestamp);
if (!g_camera.handler)
{
read->frame = g_camera.frame_num;
read->bytesread = g_camera.is_streaming ? get_video_buffer_size(g_camera.info) : 0;
}
auto& shared_data = g_fxo->get<gem_camera_shared>();
shared_data.frame_timestamp.exchange(read->timestamp);
}
return CELL_OK; return CELL_OK;
} }
@ -1624,7 +1627,7 @@ void camera_context::operator()()
if (handler && send_frame_update_event) if (handler && send_frame_update_event)
{ {
atomic_t<bool> wake_up = false; atomic_t<bool> wake_up = false;
bool result = false; camera_handler_base::camera_handler_state result = camera_handler_base::camera_handler_state::not_available;
Emu.CallAfter([&]() Emu.CallAfter([&]()
{ {
@ -1645,15 +1648,12 @@ void camera_context::operator()()
pbuf_write_index = pbuf_next_index(); pbuf_write_index = pbuf_next_index();
if (!result) send_frame_update_event = on_handler_state(result) = CELL_OK;
{
send_attach_state(false);
}
} }
} }
else else
{ {
send_frame_update_event = true; send_frame_update_event = on_handler_state(handler->get_state()) = CELL_OK;
} }
} }
@ -1731,32 +1731,21 @@ void camera_context::reset_state()
void camera_context::send_attach_state(bool attached) void camera_context::send_attach_state(bool attached)
{ {
if (!attached)
{
is_streaming = false;
is_open = false;
}
std::lock_guard lock(mutex_notify_data_map); std::lock_guard lock(mutex_notify_data_map);
if (!notify_data_map.empty()) for (const auto& [key, evt_data] : notify_data_map)
{ {
for (const auto& [key, evt_data] : notify_data_map) if (auto queue = lv2_event_queue::find(key))
{ {
if (auto queue = lv2_event_queue::find(key)) if (queue->send(evt_data.source, attached ? CELL_CAMERA_ATTACH : CELL_CAMERA_DETACH, 0, 0) == 0) [[likely]]
{ {
if (queue->send(evt_data.source, attached ? CELL_CAMERA_ATTACH : CELL_CAMERA_DETACH, 0, 0) == 0) [[likely]] is_attached = attached;
{
is_attached = attached;
}
} }
} }
} }
else
{ // We're not expected to send any events for attaching/detaching
// We're not expected to send any events for attaching/detaching is_attached = attached;
is_attached = attached;
}
} }
void camera_context::set_attr(s32 attrib, u32 arg1, u32 arg2) void camera_context::set_attr(s32 attrib, u32 arg1, u32 arg2)
@ -1817,3 +1806,52 @@ u32 camera_context::pbuf_next_index() const
// The read buffer index cannot be the same as the write index // The read buffer index cannot be the same as the write index
return (pbuf_write_index + 1u) % 2; return (pbuf_write_index + 1u) % 2;
} }
error_code camera_context::on_handler_state(camera_handler_base::camera_handler_state state)
{
switch (state)
{
case camera_handler_base::camera_handler_state::not_available:
case camera_handler_base::camera_handler_state::closed:
{
if (is_attached)
{
send_attach_state(false);
}
if (handler)
{
if (is_streaming)
{
cellCamera.warning("Camera closed or disconnected (state=%d). Trying to start camera...", static_cast<int>(state));
handler->start_camera();
}
else if (is_open)
{
cellCamera.warning("Camera closed or disconnected (state=%d). Trying to open camera...", static_cast<int>(state));
handler->open_camera();
}
}
return CELL_CAMERA_ERROR_DEVICE_NOT_FOUND;
}
case camera_handler_base::camera_handler_state::running:
{
if (!is_attached)
{
cellCamera.warning("Camera handler not attached. Sending attach event...", static_cast<int>(state));
send_attach_state(true);
}
break;
}
case camera_handler_base::camera_handler_state::open:
{
if (handler && is_streaming)
{
cellCamera.warning("Camera handler not running (state=%d). Trying to start camera...", static_cast<int>(state));
handler->start_camera();
}
break;
}
}
return CELL_OK;
}

View File

@ -442,6 +442,7 @@ public:
static constexpr auto thread_name = "Camera Thread"sv; static constexpr auto thread_name = "Camera Thread"sv;
std::shared_ptr<camera_handler_base> handler; std::shared_ptr<camera_handler_base> handler;
error_code on_handler_state(camera_handler_base::camera_handler_state state);
}; };
using camera_thread = named_thread<camera_context>; using camera_thread = named_thread<camera_context>;

View File

@ -1351,7 +1351,7 @@ error_code cellGemPrepareVideoConvert(vm::cptr<CellGemVideoConvertAttribute> vc_
return CELL_GEM_ERROR_INVALID_PARAMETER; return CELL_GEM_ERROR_INVALID_PARAMETER;
} }
const auto vc = *vc_attribute; const CellGemVideoConvertAttribute vc = *vc_attribute;
if (!vc_attribute || vc.version != CELL_GEM_VERSION) if (!vc_attribute || vc.version != CELL_GEM_VERSION)
{ {

View File

@ -7,10 +7,10 @@ class null_camera_handler final : public camera_handler_base
public: public:
null_camera_handler() : camera_handler_base() {} null_camera_handler() : camera_handler_base() {}
void open_camera() override {}; void open_camera() override { m_state = camera_handler_state::open; };
void close_camera() override {}; void close_camera() override { m_state = camera_handler_state::closed; };
void start_camera() override {}; void start_camera() override { m_state = camera_handler_state::running; };
void stop_camera() override {}; void stop_camera() override { m_state = camera_handler_state::open; };
void set_format(s32 format, u32 bytes_per_pixel) override void set_format(s32 format, u32 bytes_per_pixel) override
{ {
@ -39,12 +39,12 @@ public:
return 0; return 0;
} }
bool get_image(u8* /*buf*/, u64 /*size*/, u32& width, u32& height, u64& frame_number, u64& bytes_read) override camera_handler_state get_image(u8* /*buf*/, u64 /*size*/, u32& width, u32& height, u64& frame_number, u64& bytes_read) override
{ {
width = 0; width = 0;
height = 0; height = 0;
frame_number = 0; frame_number = 0;
bytes_read = 0; bytes_read = 0;
return true; return m_state;
} }
}; };

View File

@ -8,6 +8,7 @@ class camera_handler_base
public: public:
enum class camera_handler_state enum class camera_handler_state
{ {
not_available,
closed, closed,
open, open,
running running
@ -26,13 +27,13 @@ public:
virtual void set_mirrored(bool mirrored) = 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 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; virtual camera_handler_state 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(); }; camera_handler_state get_state() const { return m_state.load(); };
protected: protected:
std::mutex m_mutex; std::mutex m_mutex;
atomic_t<camera_handler_state> m_state = camera_handler_state::closed; atomic_t<camera_handler_state> m_state = camera_handler_state::not_available;
bool m_mirrored = false; bool m_mirrored = false;
s32 m_format = 2; // CELL_CAMERA_RAW8 s32 m_format = 2; // CELL_CAMERA_RAW8
u32 m_bytes_per_pixel = 1; u32 m_bytes_per_pixel = 1;

View File

@ -303,6 +303,9 @@
<ClCompile Include="QTGeneratedFiles\Debug\moc_pkg_install_dialog.cpp"> <ClCompile Include="QTGeneratedFiles\Debug\moc_pkg_install_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_qt_camera_error_handler.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_recvmessage_dialog_frame.cpp"> <ClCompile Include="QTGeneratedFiles\Debug\moc_recvmessage_dialog_frame.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
@ -501,6 +504,9 @@
<ClCompile Include="QTGeneratedFiles\Release\moc_pkg_install_dialog.cpp"> <ClCompile Include="QTGeneratedFiles\Release\moc_pkg_install_dialog.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_qt_camera_error_handler.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_recvmessage_dialog_frame.cpp"> <ClCompile Include="QTGeneratedFiles\Release\moc_recvmessage_dialog_frame.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</ExcludedFromBuild>
</ClCompile> </ClCompile>
@ -594,6 +600,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_error_handler.cpp" />
<ClCompile Include="rpcs3qt\qt_camera_handler.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" />
@ -1090,6 +1097,16 @@
<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>
<CustomBuild Include="rpcs3qt\qt_camera_error_handler.h">
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Identity)...</Message>
<Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp</Outputs>
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe" "%(FullPath)" -o ".\QTGeneratedFiles\$(ConfigurationName)\moc_%(Filename).cpp" -D_WINDOWS -DUNICODE -DWIN32 -DWIN64 -DQT_WIDGETS_LIB -DQT_GUI_LIB -DQT_CORE_LIB -DQT_WINEXTRAS_LIB -DQT_CONCURRENT_LIB -DQT_MULTIMEDIA_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.\debug" "-I$(QTDIR)\mkspecs\win32-msvc2015" "-I.\QTGeneratedFiles\$(ConfigurationName)" "-I.\QTGeneratedFiles" "-I$(QTDIR)\include\QtWinExtras" "-I$(QTDIR)\include\QtConcurrent" "-I$(QTDIR)\include\QtMultimedia"</Command>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
<Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Identity)...</Message>
<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 -DQT_MULTIMEDIA_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" "-I$(QTDIR)\include\QtMultimedia"</Command>
</CustomBuild>
<ClInclude Include="rpcs3qt\qt_camera_handler.h" /> <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">

View File

@ -786,6 +786,15 @@
<ClCompile Include="rpcs3qt\qt_camera_video_surface.cpp"> <ClCompile Include="rpcs3qt\qt_camera_video_surface.cpp">
<Filter>Io\camera</Filter> <Filter>Io\camera</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="rpcs3qt\qt_camera_error_handler.cpp">
<Filter>Io\camera</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Debug\moc_qt_camera_error_handler.cpp">
<Filter>Generated Files\Debug</Filter>
</ClCompile>
<ClCompile Include="QTGeneratedFiles\Release\moc_qt_camera_error_handler.cpp">
<Filter>Generated Files\Release</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="Input\ds4_pad_handler.h"> <ClInclude Include="Input\ds4_pad_handler.h">
@ -1153,6 +1162,9 @@
<CustomBuild Include="rpcs3qt\recvmessage_dialog_frame.h"> <CustomBuild Include="rpcs3qt\recvmessage_dialog_frame.h">
<Filter>Gui\message dialog</Filter> <Filter>Gui\message dialog</Filter>
</CustomBuild> </CustomBuild>
<CustomBuild Include="rpcs3qt\qt_camera_error_handler.h">
<Filter>Io\camera</Filter>
</CustomBuild>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Image Include="rpcs3.ico" /> <Image Include="rpcs3.ico" />

View File

@ -49,6 +49,7 @@ 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_error_handler.cpp
qt_camera_handler.cpp qt_camera_handler.cpp
qt_camera_video_surface.cpp qt_camera_video_surface.cpp
qt_utils.cpp qt_utils.cpp

View File

@ -0,0 +1,58 @@
#include "stdafx.h"
#include "qt_camera_error_handler.h"
LOG_CHANNEL(camera_log, "Camera");
qt_camera_error_handler::qt_camera_error_handler(std::shared_ptr<QCamera> camera, std::function<void(QCamera::Status)> status_callback)
: m_camera(std::move(camera))
, m_status_callback(std::move(status_callback))
{
if (m_camera)
{
connect(m_camera.get(), QOverload<QMultimedia::AvailabilityStatus>::of(&QCamera::availabilityChanged), this, &qt_camera_error_handler::handle_availability);
connect(m_camera.get(), &QCamera::stateChanged, this, &qt_camera_error_handler::handle_camera_state);
connect(m_camera.get(), &QCamera::statusChanged, this, &qt_camera_error_handler::handle_camera_status);
connect(m_camera.get(), &QCamera::errorOccurred, this, &qt_camera_error_handler::handle_camera_error);
connect(m_camera.get(), &QCamera::captureModeChanged, this, &qt_camera_error_handler::handle_capture_modes);
connect(m_camera.get(), QOverload<QCamera::LockStatus, QCamera::LockChangeReason>::of(&QCamera::lockStatusChanged), this, &qt_camera_error_handler::handle_lock_status);
}
}
qt_camera_error_handler::~qt_camera_error_handler()
{
}
void qt_camera_error_handler::handle_availability(QMultimedia::AvailabilityStatus availability)
{
camera_log.notice("Camera availability changed to %d", static_cast<int>(availability));
}
void qt_camera_error_handler::handle_camera_state(QCamera::State state)
{
camera_log.notice("Camera state changed to %d", static_cast<int>(state));
}
void qt_camera_error_handler::handle_camera_status(QCamera::Status status)
{
camera_log.notice("Camera status changed to %d", static_cast<int>(status));
if (m_status_callback)
{
m_status_callback(status);
}
}
void qt_camera_error_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_error_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_error_handler::handle_camera_error(QCamera::Error error)
{
camera_log.error("Error event: \"%s\" (error=%d)", m_camera ? m_camera->errorString().toStdString() : "", static_cast<int>(error));
}

View File

@ -0,0 +1,25 @@
#pragma once
#include <QObject>
#include <QCamera>
class qt_camera_error_handler : public QObject
{
Q_OBJECT
public:
qt_camera_error_handler(std::shared_ptr<QCamera> camera, std::function<void(QCamera::Status)> status_callback);
virtual ~qt_camera_error_handler();
private Q_SLOTS:
void handle_availability(QMultimedia::AvailabilityStatus availability);
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);
private:
std::shared_ptr<QCamera> m_camera;
std::function<void(QCamera::Status)> m_status_callback = nullptr;
};

View File

@ -32,21 +32,37 @@ void qt_camera_handler::set_camera(const QCameraInfo& cameraInfo)
// Create camera and video surface // Create camera and video surface
m_surface.reset(new qt_camera_video_surface(front_facing, nullptr)); m_surface.reset(new qt_camera_video_surface(front_facing, nullptr));
m_camera.reset(new QCamera(cameraInfo)); m_camera.reset(new QCamera(cameraInfo));
m_error_handler.reset(new qt_camera_error_handler(m_camera,
// Create connects (may not work due to threading) [this](QCamera::Status status)
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); }); switch (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); }); case QCamera::UnavailableStatus:
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); }); m_state = camera_handler_state::not_available;
break;
case QCamera::UnloadedStatus:
case QCamera::UnloadingStatus:
m_state = camera_handler_state::closed;
break;
case QCamera::StandbyStatus:
case QCamera::StoppingStatus:
case QCamera::LoadedStatus:
case QCamera::LoadingStatus:
m_state = camera_handler_state::open;
break;
case QCamera::StartingStatus:
case QCamera::ActiveStatus:
m_state = camera_handler_state::running;
break;
default:
camera_log.error("Ignoring unknown status %d", static_cast<int>(status));
break;
}
}));
// Set view finder and update the settings // Set view finder and update the settings
m_camera->setViewfinder(m_surface.get()); m_camera->setViewfinder(m_surface.get());
update_camera_settings(); 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() void qt_camera_handler::open_camera()
@ -65,7 +81,7 @@ void qt_camera_handler::open_camera()
if (!m_camera) if (!m_camera)
{ {
camera_log.error("No camera found"); camera_log.error("No camera found");
m_state = camera_handler_state::closed; m_state = camera_handler_state::not_available;
return; return;
} }
@ -93,8 +109,6 @@ void qt_camera_handler::open_camera()
// Update camera and view finder settings // Update camera and view finder settings
update_camera_settings(); update_camera_settings();
m_state = camera_handler_state::open;
} }
void qt_camera_handler::close_camera() void qt_camera_handler::close_camera()
@ -104,7 +118,7 @@ void qt_camera_handler::close_camera()
if (!m_camera) if (!m_camera)
{ {
camera_log.error("No camera found"); camera_log.error("No camera found");
m_state = camera_handler_state::closed; m_state = camera_handler_state::not_available;
return; return;
} }
@ -115,7 +129,6 @@ void qt_camera_handler::close_camera()
// Unload/close camera // Unload/close camera
m_camera->unload(); m_camera->unload();
m_state = camera_handler_state::closed;
} }
void qt_camera_handler::start_camera() void qt_camera_handler::start_camera()
@ -125,7 +138,7 @@ void qt_camera_handler::start_camera()
if (!m_camera) if (!m_camera)
{ {
camera_log.error("No camera found"); camera_log.error("No camera found");
m_state = camera_handler_state::closed; m_state = camera_handler_state::not_available;
return; return;
} }
@ -141,7 +154,6 @@ void qt_camera_handler::start_camera()
// Start camera. We will start receiving frames now. // Start camera. We will start receiving frames now.
m_camera->start(); m_camera->start();
m_state = camera_handler_state::running;
} }
void qt_camera_handler::stop_camera() void qt_camera_handler::stop_camera()
@ -151,7 +163,7 @@ void qt_camera_handler::stop_camera()
if (!m_camera) if (!m_camera)
{ {
camera_log.error("No camera found"); camera_log.error("No camera found");
m_state = camera_handler_state::closed; m_state = camera_handler_state::not_available;
return; return;
} }
@ -162,7 +174,6 @@ void qt_camera_handler::stop_camera()
// Stop camera. The camera will still be drawing power. // Stop camera. The camera will still be drawing power.
m_camera->stop(); m_camera->stop();
m_state = camera_handler_state::open;
} }
void qt_camera_handler::set_format(s32 format, u32 bytes_per_pixel) void qt_camera_handler::set_format(s32 format, u32 bytes_per_pixel)
@ -200,53 +211,34 @@ u64 qt_camera_handler::frame_number() const
return m_surface ? m_surface->frame_number() : 0; 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) camera_handler_base::camera_handler_state qt_camera_handler::get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read)
{ {
width = 0; width = 0;
height = 0; height = 0;
frame_number = 0; frame_number = 0;
bytes_read = 0; bytes_read = 0;
// Check for errors if (!m_camera || !m_surface)
if (!m_camera || !m_surface || m_state != camera_handler_state::running)
{ {
camera_log.error("Error: camera invalid"); camera_log.fatal("Error: camera invalid");
m_state = camera_handler_state::closed; m_state = camera_handler_state::not_available;
return false; return camera_handler_state::not_available;
} }
if (QCamera::Error error = m_camera->error(); error != QCamera::NoError) // Backup current state. State may change through events.
const camera_handler_state current_state = m_state;
if (current_state == camera_handler_state::running)
{ {
camera_log.error("Error: \"%s\" (error=%d)", m_camera ? m_camera->errorString().toStdString() : "", static_cast<int>(error)); // Copy latest image into out buffer.
m_state = camera_handler_state::closed; m_surface->get_image(buf, size, width, height, frame_number, bytes_read);
return false; }
else
{
camera_log.error("Camera not running (m_state=%d)", static_cast<int>(current_state));
} }
switch (QCamera::Status status = m_camera->status()) return current_state;
{
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() void qt_camera_handler::update_camera_settings()
@ -306,28 +298,3 @@ void qt_camera_handler::update_camera_settings()
m_surface->set_mirrored(m_mirrored); 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

@ -2,20 +2,19 @@
#include "Emu/Io/camera_handler_base.h" #include "Emu/Io/camera_handler_base.h"
#include "qt_camera_video_surface.h" #include "qt_camera_video_surface.h"
#include "qt_camera_error_handler.h"
#include <QCamera> #include <QCamera>
#include <QCameraImageCapture> #include <QCameraImageCapture>
#include <QAbstractVideoSurface> #include <QAbstractVideoSurface>
class video_surface; class qt_camera_handler final : public camera_handler_base
class qt_camera_handler final : public camera_handler_base, public QObject
{ {
public: public:
qt_camera_handler(); qt_camera_handler();
virtual ~qt_camera_handler(); virtual ~qt_camera_handler();
void set_camera(const QCameraInfo &cameraInfo); void set_camera(const QCameraInfo& cameraInfo);
void open_camera() override; void open_camera() override;
void close_camera() override; void close_camera() override;
@ -26,16 +25,12 @@ public:
void set_resolution(u32 width, u32 height) override; void set_resolution(u32 width, u32 height) override;
void set_mirrored(bool mirrored) override; void set_mirrored(bool mirrored) override;
u64 frame_number() const override; u64 frame_number() const override;
bool get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read) override; camera_handler_state get_image(u8* buf, u64 size, u32& width, u32& height, u64& frame_number, u64& bytes_read) override;
private: 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(); void update_camera_settings();
std::unique_ptr<QCamera> m_camera; std::shared_ptr<QCamera> m_camera;
std::unique_ptr<qt_camera_video_surface> m_surface; std::unique_ptr<qt_camera_video_surface> m_surface;
std::unique_ptr<qt_camera_error_handler> m_error_handler;
}; };