Cemu/src/Cafe/OS/libs/camera/camera.cpp
2025-03-24 06:33:41 +00:00

285 lines
7 KiB
C++

#include "Common/precompiled.h"
#include "Cafe/OS/common/OSCommon.h"
#include "camera.h"
#include "Cafe/OS/RPL/rpl.h"
#include "Cafe/OS/libs/coreinit/coreinit_Alarm.h"
#include "Cafe/OS/libs/coreinit/coreinit_Time.h"
#include "Cafe/HW/Espresso/PPCCallback.h"
#include "util/helpers/helpers.h"
#include "util/helpers/ringbuffer.h"
#include "camera/CameraManager.h"
namespace camera
{
constexpr unsigned CAMERA_WIDTH = 640;
constexpr unsigned CAMERA_HEIGHT = 480;
enum class CAMStatus : sint32
{
Success = 0,
InvalidArg = -1,
InvalidHandle = -2,
SurfaceQueueFull = -4,
InsufficientMemory = -5,
NotReady = -6,
Uninitialized = -8,
UVCError = -9,
DecoderInitFailed = -10,
DeviceInUse = -12,
DecoderSessionFailed = -13,
};
enum class CAMFps : uint32
{
_15 = 0,
_30 = 1
};
enum class CAMEventType : uint32
{
Decode = 0,
Detached = 1
};
enum class CAMForceDisplay
{
None = 0,
DRC = 1
};
enum class CAMImageType : uint32
{
Default = 0
};
struct CAMImageInfo
{
betype<CAMImageType> type;
uint32be height;
uint32be width;
};
static_assert(sizeof(CAMImageInfo) == 0x0C);
struct CAMInitInfo_t
{
CAMImageInfo imageInfo;
uint32be workMemorySize;
MEMPTR<void> workMemoryData;
MEMPTR<void> callback;
betype<CAMForceDisplay> forceDisplay;
betype<CAMFps> fps;
uint32be threadFlags;
uint8 unk[0x10];
};
static_assert(sizeof(CAMInitInfo_t) == 0x34);
struct CAMTargetSurface
{
/* +0x00 */ sint32be size;
/* +0x04 */ MEMPTR<uint8_t> data;
/* +0x08 */ uint32be height;
/* +0x0C */ uint32be width;
/* +0x10 */ uint32be ukn10;
/* +0x14 */ uint32be ukn14;
/* +0x18 */ uint32be ukn18;
/* +0x1C */ uint32be ukn1C;
};
struct CAMDecodeEventParam
{
betype<CAMEventType> type;
MEMPTR<void> data;
uint32be channel;
uint32be errored;
};
static_assert(sizeof(CAMDecodeEventParam) == 0x10);
constexpr static int32_t CAM_HANDLE = 0;
struct
{
std::recursive_mutex mutex{};
bool initialized = false;
bool shouldTriggerCallback = false;
std::atomic_bool isOpen = false;
MEMPTR<void> eventCallback = nullptr;
RingBuffer<MEMPTR<uint8_t>, 20> inTargetBuffers{};
RingBuffer<MEMPTR<uint8_t>, 20> outTargetBuffers{};
std::thread updateThread;
} s_instance;
SysAllocator<CAMDecodeEventParam> s_cameraEventData;
SysAllocator<coreinit::OSAlarm_t> s_cameraAlarm;
void UpdateLoop()
{
while (s_instance.isOpen)
{
{
std::scoped_lock lock(s_instance.mutex);
auto surfaceBuffer = s_instance.inTargetBuffers.Pop();
if (surfaceBuffer.IsNull())
{
continue;
}
CameraManager::instance().GetNV12Data(surfaceBuffer.GetPtr());
s_instance.outTargetBuffers.Push(surfaceBuffer);
}
}
}
void AlarmCallback(PPCInterpreter_t*)
{
s_cameraEventData->type = CAMEventType::Decode;
s_cameraEventData->channel = 0;
if (s_instance.shouldTriggerCallback)
{
s_instance.shouldTriggerCallback = false;
s_cameraEventData->data = nullptr;
s_cameraEventData->errored = false;
}
else if (s_instance.isOpen)
{
if (auto buffer = s_instance.outTargetBuffers.Pop())
{
s_cameraEventData->data = buffer;
s_cameraEventData->errored = false;
PPCCoreCallback(s_instance.eventCallback, s_cameraEventData.GetMPTR());
}
else
{
s_cameraEventData->data = nullptr;
s_cameraEventData->errored = true;
}
}
else
return;
}
sint32 CAMGetMemReq(CAMImageInfo*)
{
return 1 * 1024; // always return 1KB
}
CAMStatus CAMCheckMemSegmentation(void* base, uint32 size)
{
if (!base || size == 0)
return CAMStatus::InvalidArg;
return CAMStatus::Success;
}
sint32 CAMInit(uint32 cameraId, CAMInitInfo_t* initInfo, betype<CAMStatus>* error)
{
*error = CAMStatus::Success;
std::scoped_lock lock(s_instance.mutex);
if (s_instance.initialized)
{
*error = CAMStatus::DeviceInUse;
return -1;
}
if (!initInfo || !initInfo->workMemoryData ||
!match_any_of(initInfo->forceDisplay, CAMForceDisplay::None, CAMForceDisplay::DRC) ||
!match_any_of(initInfo->fps, CAMFps::_15, CAMFps::_30) ||
initInfo->imageInfo.type != CAMImageType::Default)
{
*error = CAMStatus::InvalidArg;
return -1;
}
cemu_assert_debug(initInfo->forceDisplay != CAMForceDisplay::DRC);
cemu_assert_debug(initInfo->workMemorySize != 0);
cemu_assert_debug(initInfo->imageInfo.type == CAMImageType::Default);
auto frameRate = initInfo->fps == CAMFps::_15 ? 15 : 30;
s_instance.initialized = true;
s_instance.shouldTriggerCallback = true;
s_instance.eventCallback = initInfo->callback;
coreinit::OSCreateAlarm(s_cameraAlarm.GetPtr());
coreinit::OSSetPeriodicAlarm(s_cameraAlarm.GetPtr(),
coreinit::OSGetTime(),
coreinit::EspressoTime::GetTimerClock() / frameRate,
RPLLoader_MakePPCCallable(AlarmCallback));
return 0;
}
CAMStatus CAMClose(sint32 camHandle)
{
if (camHandle != CAM_HANDLE)
return CAMStatus::InvalidHandle;
{
std::scoped_lock lock(s_instance.mutex);
if (!s_instance.initialized || !s_instance.isOpen)
return CAMStatus::Uninitialized;
s_instance.isOpen = false;
}
s_instance.updateThread.join();
CameraManager::instance().Close();
return CAMStatus::Success;
}
CAMStatus CAMOpen(sint32 camHandle)
{
if (camHandle != CAM_HANDLE)
return CAMStatus::InvalidHandle;
auto lock = std::scoped_lock(s_instance.mutex);
if (!s_instance.initialized)
return CAMStatus::Uninitialized;
if (s_instance.isOpen)
return CAMStatus::DeviceInUse;
if (!CameraManager::instance().Open(false))
return CAMStatus::UVCError;
s_instance.inTargetBuffers.Clear();
s_instance.outTargetBuffers.Clear();
s_instance.isOpen = true;
s_instance.updateThread = std::thread(UpdateLoop);
return CAMStatus::Success;
}
CAMStatus CAMSubmitTargetSurface(sint32 camHandle, CAMTargetSurface* targetSurface)
{
if (camHandle != CAM_HANDLE)
return CAMStatus::InvalidHandle;
if (!targetSurface || targetSurface->data.IsNull() || targetSurface->size < 1)
return CAMStatus::InvalidArg;
auto lock = std::scoped_lock(s_instance.mutex);
if (!s_instance.initialized)
return CAMStatus::Uninitialized;
if (!s_instance.inTargetBuffers.Push(targetSurface->data))
return CAMStatus::SurfaceQueueFull;
return CAMStatus::Success;
}
void CAMExit(sint32 camHandle)
{
if (camHandle != CAM_HANDLE)
return;
std::scoped_lock lock(s_instance.mutex);
if (!s_instance.initialized)
return;
if (s_instance.isOpen)
CAMClose(camHandle);
s_instance.initialized = false;
s_instance.shouldTriggerCallback = false;
coreinit::OSCancelAlarm(s_cameraAlarm);
}
void reset()
{
CAMExit(0);
}
void load()
{
reset();
cafeExportRegister("camera", CAMGetMemReq, LogType::Force);
cafeExportRegister("camera", CAMCheckMemSegmentation, LogType::Force);
cafeExportRegister("camera", CAMInit, LogType::Force);
cafeExportRegister("camera", CAMExit, LogType::Force);
cafeExportRegister("camera", CAMOpen, LogType::Force);
cafeExportRegister("camera", CAMClose, LogType::Force);
cafeExportRegister("camera", CAMSubmitTargetSurface, LogType::Force);
}
} // namespace camera