mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-23 09:03:22 -05:00
416 lines
15 KiB
C++
416 lines
15 KiB
C++
#include <cstdint>
|
|
#include <cstddef>
|
|
#include <cstring>
|
|
#include <map>
|
|
#include <mutex>
|
|
#include <stdexcept>
|
|
#include <vector>
|
|
#include "SceTypes.hpp"
|
|
#include "prx/libc/include/General.hpp"
|
|
|
|
namespace {
|
|
|
|
constexpr int VideodecErrorApiFail = static_cast<int>(0x80C10000);
|
|
constexpr int VideodecErrorStructSize = static_cast<int>(0x80C10002);
|
|
constexpr int VideodecErrorHandle = static_cast<int>(0x80C10003);
|
|
constexpr int VideodecErrorAuSize = static_cast<int>(0x80C10009);
|
|
constexpr int VideodecErrorAuPointer = static_cast<int>(0x80C1000A);
|
|
constexpr int VideodecErrorFrameBufferSize = static_cast<int>(0x80C1000B);
|
|
constexpr int VideodecErrorFrameBufferPointer = static_cast<int>(0x80C1000C);
|
|
constexpr int VideodecErrorConfigInfo = static_cast<int>(0x80C1000E);
|
|
constexpr int VideodecErrorArgumentPointer = static_cast<int>(0x80C1000F);
|
|
constexpr int VideodecErrorDecodeAu = static_cast<int>(0x80C10011);
|
|
|
|
constexpr std::uint32_t CodecAvc = 1;
|
|
constexpr std::uint32_t PitchAlignment = 64;
|
|
|
|
struct ConfigInfo {
|
|
std::uint64_t thisSize;
|
|
std::uint32_t codecType;
|
|
std::uint32_t profile;
|
|
std::uint32_t maxLevel;
|
|
std::int32_t maxFrameWidth;
|
|
std::int32_t maxFrameHeight;
|
|
std::int32_t maxDpbFrameCount;
|
|
std::uint64_t videodecFlags;
|
|
};
|
|
|
|
struct ResourceInfo {
|
|
std::uint64_t thisSize;
|
|
std::uint64_t cpuMemorySize;
|
|
void* cpuMemory;
|
|
std::uint64_t cpuGpuMemorySize;
|
|
void* cpuGpuMemory;
|
|
std::uint64_t maxFrameBufferSize;
|
|
std::uint32_t frameBufferAlignment;
|
|
};
|
|
|
|
struct Ctrl {
|
|
std::uint64_t thisSize;
|
|
void* handle;
|
|
std::uint64_t version;
|
|
};
|
|
|
|
struct FrameBuffer {
|
|
std::uint64_t thisSize;
|
|
void* frameBuffer;
|
|
std::uint64_t frameBufferSize;
|
|
};
|
|
|
|
struct AvcInfo {
|
|
std::uint32_t numUnitsInTick;
|
|
std::uint32_t timeScale;
|
|
std::uint8_t fixedFrameRateFlag;
|
|
std::uint8_t aspectRatioIdc;
|
|
std::uint16_t sarWidth;
|
|
std::uint16_t sarHeight;
|
|
std::uint8_t colourPrimaries;
|
|
std::uint8_t transferCharacteristics;
|
|
std::uint8_t matrixCoefficients;
|
|
std::uint8_t videoFullRangeFlag;
|
|
std::uint32_t frameCropLeftOffset;
|
|
std::uint32_t frameCropRightOffset;
|
|
std::uint32_t frameCropTopOffset;
|
|
std::uint32_t frameCropBottomOffset;
|
|
};
|
|
|
|
union CodecInfo {
|
|
std::uint8_t reserved[64];
|
|
AvcInfo avc;
|
|
};
|
|
|
|
struct PictureInfo {
|
|
std::uint64_t thisSize;
|
|
std::uint32_t isValid;
|
|
std::uint32_t codecType;
|
|
std::uint32_t frameWidth;
|
|
std::uint32_t framePitch;
|
|
std::uint32_t frameHeight;
|
|
std::uint32_t isErrorPic;
|
|
std::uint64_t ptsData;
|
|
std::uint64_t attachedData;
|
|
CodecInfo codec;
|
|
};
|
|
|
|
struct InputData {
|
|
std::uint64_t thisSize;
|
|
const std::uint8_t* auData;
|
|
std::uint64_t auSize;
|
|
std::uint64_t ptsData;
|
|
std::uint64_t dtsData;
|
|
std::uint64_t attachedData;
|
|
};
|
|
|
|
struct Decoder2ConfigInfo {
|
|
std::uint64_t thisSize;
|
|
std::uint32_t resourceType;
|
|
std::uint32_t codecType;
|
|
std::uint32_t profile;
|
|
std::uint32_t maxLevel;
|
|
std::int32_t maxFrameWidth;
|
|
std::int32_t maxFrameHeight;
|
|
std::int32_t maxDpbFrameCount;
|
|
std::uint32_t decodePipelineDepth;
|
|
};
|
|
|
|
struct Decoder2MemoryInfo {
|
|
std::uint64_t thisSize;
|
|
std::uint64_t cpuMemorySize;
|
|
void* cpuMemory;
|
|
std::uint64_t gpuMemorySize;
|
|
void* gpuMemory;
|
|
std::uint64_t cpuGpuMemorySize;
|
|
void* cpuGpuMemory;
|
|
std::uint64_t maxFrameBufferSize;
|
|
std::uint32_t frameBufferAlignment;
|
|
};
|
|
|
|
struct Decoder2FrameBuffer {
|
|
std::uint64_t thisSize;
|
|
void* frameBuffer;
|
|
std::uint64_t frameBufferSize;
|
|
bool isAccepted;
|
|
};
|
|
|
|
struct Decoder2OutputInfo {
|
|
std::uint64_t thisSize;
|
|
bool isValid;
|
|
bool isErrorFrame;
|
|
std::uint8_t pictureCount;
|
|
std::uint32_t codecType;
|
|
std::uint32_t frameWidth;
|
|
std::uint32_t framePitch;
|
|
std::uint32_t frameHeight;
|
|
void* frameBuffer;
|
|
std::uint64_t frameBufferSize;
|
|
std::uint32_t frameFormat;
|
|
std::uint32_t framePitchInBytes;
|
|
};
|
|
|
|
struct Decoder2AvcPictureInfo {
|
|
std::uint64_t thisSize;
|
|
bool isValid;
|
|
std::uint64_t ptsData;
|
|
std::uint64_t dtsData;
|
|
std::uint64_t attachedData;
|
|
std::uint8_t idrPictureFlag;
|
|
std::uint8_t profileIdc;
|
|
std::uint8_t levelIdc;
|
|
std::uint32_t picWidthInLumaSamples;
|
|
std::uint32_t picHeightInLumaSamples;
|
|
};
|
|
|
|
struct Decoder2Crop {
|
|
std::uint32_t left;
|
|
std::uint32_t right;
|
|
std::uint32_t top;
|
|
std::uint32_t bottom;
|
|
};
|
|
|
|
static_assert(sizeof(ConfigInfo) == 40 && sizeof(ResourceInfo) == 56 && sizeof(Ctrl) == 24);
|
|
static_assert(sizeof(FrameBuffer) == 24 && sizeof(InputData) == 48 && sizeof(PictureInfo) == 112);
|
|
static_assert(sizeof(CodecInfo) == 64 && offsetof(PictureInfo, codec) == 48);
|
|
|
|
struct Decoder {
|
|
std::uint64_t handle = 0;
|
|
std::vector<std::uint8_t> picture;
|
|
};
|
|
|
|
std::mutex decodersMutex;
|
|
std::map<const void*, Decoder*> decoders;
|
|
|
|
Decoder* FindDecoder(const void* handle) {
|
|
if (handle == nullptr) return nullptr;
|
|
std::lock_guard lock(decodersMutex);
|
|
const auto found = decoders.find(handle);
|
|
return found == decoders.end() ? nullptr : found->second;
|
|
}
|
|
|
|
std::uint32_t AlignUp(std::uint32_t value, std::uint32_t alignment) {
|
|
return (value + alignment - 1) / alignment * alignment;
|
|
}
|
|
|
|
}
|
|
|
|
extern "C" {
|
|
int APS5_VABI sceVideodec2QueryDecoderMemoryInfo_nid_postfix(const Decoder2ConfigInfo*, Decoder2MemoryInfo*);
|
|
int APS5_VABI sceVideodec2CreateDecoder_nid_postfix(const Decoder2ConfigInfo*, const Decoder2MemoryInfo*, std::uint64_t*);
|
|
int APS5_VABI sceVideodec2DeleteDecoder_nid_postfix(std::uint64_t);
|
|
int APS5_VABI sceVideodec2Decode_nid_postfix(std::uint64_t, const InputData*, Decoder2FrameBuffer*, Decoder2OutputInfo*);
|
|
int APS5_VABI sceVideodec2Flush_nid_postfix(std::uint64_t, Decoder2FrameBuffer*, Decoder2OutputInfo*);
|
|
int APS5_VABI sceVideodec2Reset_nid_postfix(std::uint64_t);
|
|
int APS5_VABI sceVideodec2GetAvcPictureInfo_nid_postfix(const Decoder2OutputInfo*, Decoder2AvcPictureInfo*, Decoder2AvcPictureInfo*);
|
|
void Videodec2PictureCrop_nid_no_patch(std::uint64_t, const void*, Decoder2Crop*);
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct Geometry {
|
|
std::uint32_t width = 0;
|
|
std::uint32_t height = 0;
|
|
std::uint32_t pitch = 0;
|
|
Decoder2Crop crop{};
|
|
};
|
|
|
|
Geometry Measure(const Decoder2OutputInfo& output, const Decoder2Crop& crop) {
|
|
Geometry geometry;
|
|
geometry.width = output.frameWidth;
|
|
geometry.height = output.frameHeight;
|
|
geometry.crop = crop;
|
|
geometry.pitch = AlignUp(geometry.width, PitchAlignment);
|
|
return geometry;
|
|
}
|
|
|
|
void FillPictureInfo(const Geometry& geometry, const Decoder2OutputInfo& output, PictureInfo* picture) {
|
|
picture->isValid = 0;
|
|
picture->codecType = 0;
|
|
picture->frameWidth = 0;
|
|
picture->framePitch = 0;
|
|
picture->frameHeight = 0;
|
|
picture->isErrorPic = 0;
|
|
picture->ptsData = 0;
|
|
picture->attachedData = 0;
|
|
std::memset(picture->codec.reserved, 0, sizeof(picture->codec.reserved));
|
|
if (!output.isValid) return;
|
|
picture->isValid = 1;
|
|
picture->frameWidth = geometry.width;
|
|
picture->framePitch = geometry.pitch;
|
|
picture->frameHeight = geometry.height;
|
|
picture->isErrorPic = output.isErrorFrame ? 1 : 0;
|
|
picture->codec.avc.frameCropLeftOffset = geometry.crop.left / 2;
|
|
picture->codec.avc.frameCropRightOffset = geometry.crop.right / 2;
|
|
picture->codec.avc.frameCropTopOffset = geometry.crop.top / 2;
|
|
picture->codec.avc.frameCropBottomOffset = geometry.crop.bottom / 2;
|
|
}
|
|
|
|
int CopyPicture(const Decoder2OutputInfo& output, const Geometry& geometry, const FrameBuffer* frameBuffer) {
|
|
const auto bytes = static_cast<std::uint64_t>(geometry.pitch) * geometry.height * 3 / 2;
|
|
if (frameBuffer->frameBuffer == nullptr) return VideodecErrorFrameBufferPointer;
|
|
if (frameBuffer->frameBufferSize < bytes) return VideodecErrorFrameBufferSize;
|
|
const auto sourcePitch = static_cast<std::uint64_t>(output.framePitchInBytes);
|
|
const auto* source = static_cast<const std::uint8_t*>(output.frameBuffer);
|
|
auto* destination = static_cast<std::uint8_t*>(frameBuffer->frameBuffer);
|
|
for (std::uint32_t row = 0; row < geometry.height; ++row)
|
|
std::memcpy(destination + static_cast<std::uint64_t>(row) * geometry.pitch, source + static_cast<std::uint64_t>(row) * sourcePitch, geometry.width);
|
|
const auto* sourceChroma = source + sourcePitch * geometry.height;
|
|
auto* destinationChroma = destination + static_cast<std::uint64_t>(geometry.pitch) * geometry.height;
|
|
for (std::uint32_t row = 0; row < geometry.height / 2; ++row)
|
|
std::memcpy(destinationChroma + static_cast<std::uint64_t>(row) * geometry.pitch, sourceChroma + static_cast<std::uint64_t>(row) * sourcePitch, geometry.width);
|
|
return 0;
|
|
}
|
|
|
|
}
|
|
|
|
extern "C" {
|
|
|
|
int APS5_VABI sceVideodecCreateDecoder(const ConfigInfo* config, const ResourceInfo* resource, Ctrl* ctrl) {
|
|
if (config == nullptr || resource == nullptr || ctrl == nullptr) return VideodecErrorArgumentPointer;
|
|
if (config->thisSize < sizeof(ConfigInfo) || resource->thisSize < sizeof(ResourceInfo)) return VideodecErrorStructSize;
|
|
Decoder2ConfigInfo decoderConfig{};
|
|
decoderConfig.thisSize = sizeof(Decoder2ConfigInfo);
|
|
decoderConfig.resourceType = 1;
|
|
decoderConfig.codecType = CodecAvc;
|
|
decoderConfig.profile = config->profile;
|
|
decoderConfig.maxLevel = config->maxLevel;
|
|
decoderConfig.maxFrameWidth = config->maxFrameWidth;
|
|
decoderConfig.maxFrameHeight = config->maxFrameHeight;
|
|
decoderConfig.maxDpbFrameCount = config->maxDpbFrameCount;
|
|
decoderConfig.decodePipelineDepth = 1;
|
|
Decoder2MemoryInfo decoderMemory{};
|
|
decoderMemory.thisSize = sizeof(Decoder2MemoryInfo);
|
|
decoderMemory.cpuMemorySize = resource->cpuMemorySize;
|
|
decoderMemory.cpuMemory = resource->cpuMemory;
|
|
decoderMemory.cpuGpuMemorySize = resource->cpuGpuMemorySize;
|
|
decoderMemory.cpuGpuMemory = resource->cpuGpuMemory;
|
|
decoderMemory.maxFrameBufferSize = resource->maxFrameBufferSize;
|
|
decoderMemory.frameBufferAlignment = resource->frameBufferAlignment;
|
|
try {
|
|
sceVideodec2QueryDecoderMemoryInfo_nid_postfix(&decoderConfig, &decoderMemory);
|
|
} catch (const std::exception&) {
|
|
return VideodecErrorConfigInfo;
|
|
}
|
|
std::uint64_t handle = 0;
|
|
if (sceVideodec2CreateDecoder_nid_postfix(&decoderConfig, &decoderMemory, &handle) != 0) return VideodecErrorApiFail;
|
|
auto* decoder = new Decoder();
|
|
decoder->handle = handle;
|
|
decoder->picture.resize(decoderMemory.maxFrameBufferSize);
|
|
{
|
|
std::lock_guard lock(decodersMutex);
|
|
decoders.emplace(decoder, decoder);
|
|
}
|
|
ctrl->thisSize = sizeof(Ctrl);
|
|
ctrl->handle = decoder;
|
|
ctrl->version = 1;
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceVideodecDecode(Ctrl* ctrl, const InputData* input, FrameBuffer* frameBuffer, PictureInfo* picture) {
|
|
if (ctrl == nullptr || input == nullptr || picture == nullptr) return VideodecErrorArgumentPointer;
|
|
if (frameBuffer == nullptr) return VideodecErrorFrameBufferPointer;
|
|
if (ctrl->thisSize < sizeof(Ctrl)) return VideodecErrorStructSize;
|
|
if (input->thisSize < sizeof(InputData)) return VideodecErrorStructSize;
|
|
if (picture->thisSize < sizeof(PictureInfo)) return VideodecErrorStructSize;
|
|
if (frameBuffer->thisSize < sizeof(FrameBuffer)) return VideodecErrorStructSize;
|
|
auto* decoder = FindDecoder(ctrl->handle);
|
|
if (decoder == nullptr) return VideodecErrorHandle;
|
|
if (input->auData == nullptr) return VideodecErrorAuPointer;
|
|
if (input->auSize == 0) return VideodecErrorAuSize;
|
|
if (frameBuffer->frameBuffer == nullptr) return VideodecErrorFrameBufferPointer;
|
|
Decoder2FrameBuffer target{};
|
|
target.thisSize = sizeof(Decoder2FrameBuffer);
|
|
target.frameBuffer = decoder->picture.data();
|
|
target.frameBufferSize = decoder->picture.size();
|
|
Decoder2OutputInfo output{sizeof(Decoder2OutputInfo)};
|
|
try {
|
|
sceVideodec2Decode_nid_postfix(decoder->handle, input, &target, &output);
|
|
} catch (const std::exception&) {
|
|
return VideodecErrorDecodeAu;
|
|
}
|
|
Decoder2Crop crop{};
|
|
if (output.isValid) Videodec2PictureCrop_nid_no_patch(decoder->handle, output.frameBuffer, &crop);
|
|
const auto geometry = Measure(output, crop);
|
|
Decoder2AvcPictureInfo avc{sizeof(Decoder2AvcPictureInfo)};
|
|
if (output.isValid) sceVideodec2GetAvcPictureInfo_nid_postfix(&output, &avc, nullptr);
|
|
FillPictureInfo(geometry, output, picture);
|
|
picture->ptsData = avc.ptsData;
|
|
picture->attachedData = avc.attachedData;
|
|
if (!output.isValid) return 0;
|
|
return CopyPicture(output, geometry, frameBuffer);
|
|
}
|
|
|
|
int APS5_VABI sceVideodecDeleteDecoder(Ctrl* ctrl) {
|
|
if (ctrl == nullptr) return VideodecErrorArgumentPointer;
|
|
if (ctrl->thisSize < sizeof(Ctrl)) return VideodecErrorStructSize;
|
|
Decoder* decoder = nullptr;
|
|
{
|
|
std::lock_guard lock(decodersMutex);
|
|
const auto found = decoders.find(ctrl->handle);
|
|
if (found == decoders.end()) return VideodecErrorHandle;
|
|
decoder = found->second;
|
|
decoders.erase(found);
|
|
}
|
|
const auto handle = decoder->handle;
|
|
delete decoder;
|
|
try {
|
|
sceVideodec2DeleteDecoder_nid_postfix(handle);
|
|
} catch (const std::exception&) {
|
|
return VideodecErrorApiFail;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceVideodecFlush(Ctrl* ctrl, FrameBuffer* frameBuffer, PictureInfo* picture) {
|
|
if (ctrl == nullptr || picture == nullptr) return VideodecErrorArgumentPointer;
|
|
if (frameBuffer == nullptr) return VideodecErrorFrameBufferPointer;
|
|
if (ctrl->thisSize < sizeof(Ctrl)) return VideodecErrorStructSize;
|
|
if (picture->thisSize < sizeof(PictureInfo)) return VideodecErrorStructSize;
|
|
if (frameBuffer->thisSize < sizeof(FrameBuffer)) return VideodecErrorStructSize;
|
|
auto* decoder = FindDecoder(ctrl->handle);
|
|
if (decoder == nullptr) return VideodecErrorHandle;
|
|
if (frameBuffer->frameBuffer == nullptr) return VideodecErrorFrameBufferPointer;
|
|
Decoder2FrameBuffer target{};
|
|
target.thisSize = sizeof(Decoder2FrameBuffer);
|
|
target.frameBuffer = decoder->picture.data();
|
|
target.frameBufferSize = decoder->picture.size();
|
|
Decoder2OutputInfo output{sizeof(Decoder2OutputInfo)};
|
|
try {
|
|
sceVideodec2Flush_nid_postfix(decoder->handle, &target, &output);
|
|
} catch (const std::exception&) {
|
|
return VideodecErrorDecodeAu;
|
|
}
|
|
Decoder2Crop crop{};
|
|
if (output.isValid) Videodec2PictureCrop_nid_no_patch(decoder->handle, output.frameBuffer, &crop);
|
|
const auto geometry = Measure(output, crop);
|
|
Decoder2AvcPictureInfo avc{sizeof(Decoder2AvcPictureInfo)};
|
|
if (output.isValid) sceVideodec2GetAvcPictureInfo_nid_postfix(&output, &avc, nullptr);
|
|
FillPictureInfo(geometry, output, picture);
|
|
picture->ptsData = avc.ptsData;
|
|
picture->attachedData = avc.attachedData;
|
|
if (!output.isValid) return 0;
|
|
return CopyPicture(output, geometry, frameBuffer);
|
|
}
|
|
|
|
int APS5_VABI sceVideodecReset(Ctrl* ctrl) {
|
|
if (ctrl == nullptr) return VideodecErrorArgumentPointer;
|
|
if (ctrl->thisSize < sizeof(Ctrl)) return VideodecErrorStructSize;
|
|
auto* decoder = FindDecoder(ctrl->handle);
|
|
if (decoder == nullptr) return VideodecErrorHandle;
|
|
try {
|
|
sceVideodec2Reset_nid_postfix(decoder->handle);
|
|
} catch (const std::exception&) {
|
|
return VideodecErrorApiFail;
|
|
}
|
|
return 0;
|
|
}
|
|
int APS5_VABI sceVideodecMapMemory(void) {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
int APS5_VABI sceVideodecQueryResourceInfo(void) {
|
|
NotImplemented_nid_no_patch(__func__);
|
|
return 0;
|
|
}
|
|
|
|
}
|